1

I want to create new methods for str datatype. Here's what I tried

class str(str):
    def __init__(self) -> None:
        super().__init__()

    def work(self):
        # Just for testing
        print("works")

Here, when I initialize strings using str() this works but wrapping them around simple quotation this raises the error AttributeError: 'str' object has no attribute 'work'

Like this:

b = str("Hello world")
b.work()

Works as intended with stdout "works"

but,

a = "Hello world"
a.work()

Raises AttributeError: 'str' object has no attribute 'work'

I want to create new methods such that they work with these cases :

"foo".work()
str("foo").work() # <- This actually works :D
bar = "foo"; bar.work()

Thanks for the help.

Grismar
  • 27,561
  • 4
  • 31
  • 54
Achxy_
  • 1,171
  • 1
  • 5
  • 25
  • 4
    I would *strongly* recommend not doing this in this way. Maybe create a new class `Str` that's a subclass of `str`, but don't call the subclass the same thing as the parent class, as it will only lead to confusion when reading your code, and you'll run into problems like this. – MattDMo Jan 16 '22 at 05:29
  • Is this in part a spelling issue? `self.words` instead of `self.works`? – esramish Jan 16 '22 at 05:30
  • The various ways in which Python creates string objects, such as quoted literals in your source code, directly call the C-language implementation of the `str` class - the name "str" is not looked up in the process, there is absolutely nothing you can do from Python code to change this behavior. – jasonharper Jan 16 '22 at 05:32
  • 2
    not really, the `words` was supposed to be used later. I should edit the post to avoid confusion. Thanks for the comment, @esramish. – Achxy_ Jan 16 '22 at 05:33
  • You really can't. Is there some specific problem that you are trying to solve? There are various alternatives, but which is best depends on what you want to do. – MisterMiyagi Jan 16 '22 at 06:11
  • There isn't anything that I am trying to solve. This question just arose when I tried poking around at strings and instances of `str` behaved differently from the strings that were originated from enclosing within quotations despite both being the same when I checked with `isinstance(..., str)`. I thought this was very interesting and wanted to know more, @MisterMiyagi. – Achxy_ Jan 16 '22 at 07:22
  • Have a look at the duplicate. This is possible to do using implementation knowledge, or the ``forbiddenfruit`` package (which basically knows how these types work). – MisterMiyagi Jan 16 '22 at 09:54

1 Answers1

1

This can't be done, since str is one of the immutable types in Python. Among the immutable types are bool, int, float, tuple, string, frozenset and perhaps a few more I don't know about.

With other types, you might be able to do something like this:

class MyClass:
    def __str__(self):
        return "my class"


def print_me_quoted(self):
    print(f'"{self}"')

mc_old = MyClass()
MyClass.print_me_quoted = print_me_quoted
mc_new = MyClass()

# both now have it
mc_old.print_me_quoted()
mc_new.print_me_quoted()

It's still a spectacularly bad idea, to change the behaviour of a class by monkey-patching it like this, but it can be done. Expect your editor or IDE not to like it, or to understand it - you'll see warnings.

If you try this with str:

def print_me_quoted(self):
    print(f'"{self}"')


str.print_me_quoted = print_me_quoted
mc = str(10)
mc.print_me_quoted()

You get this TypeError:

TypeError: cannot set 'print_me_quoted' attribute of immutable type 'str'

You could do it on your own version of str:

class Str(str):
    ...


def print_me_quoted(self):
    print(f'"{self}"')


Str.print_me_quoted = print_me_quoted
mc = Str(10)
mc.print_me_quoted()

But of course that does not change the standard behaviour of strings - which is exactly the point.

Grismar
  • 27,561
  • 4
  • 31
  • 54