Why I have problem creating a class inheriting from str (or also from int)
class C(str): def __init__(self, a, b): str.__init__(self,a) self.b = b C("a", "B") TypeError: str() takes at most 1 argument (2 given)
the same happens if I try to use
int instead of
str, but it works with custom classes. I need to use
__new__ instead of
__new__ in case of immutable types:
class C(str): def __new__(cls, content, b): return str.__new__(cls, content) def __str__(self): return str.__str__(self) a=C("hello", "world") print a
Python strings are immutable types. The function
__new__ is called to create a new instance of object
C. The python
__new__ function is basically exists to allow inheritance from immutable types.
>>> class C(str): ... def __new__(cls, *args, **kw): ... return str.__new__(cls, *args, **kw) ... >>> c = C("hello world") >>> type(c) <class '__main__.C'> >>> c.__class__.__mro__ (<class '__main__.C'>, <type 'str'>, <type 'basestring'>, <type 'object'>)
__init__ is called after the object is constructed, it is too late to modify the value for immutable types. Note that
__new__ is a classmethod, so I have called the first parameter
See here for more information
>>> class C(str): ... def __new__(cls, value, meta): ... obj = str.__new__(cls, value) ... obj.meta = meta ... return obj ... >>> c = C("hello world", "meta") >>> c 'hello world' >>> c.meta 'meta'
Inheriting built-in types is very seldom worth while. You have to deal with several issues and you don’t really get much benefit.
It is almost always better to use composition. Instead of inheriting
str, you would keep a
str object as an attribute.
class EnhancedString(object): def __init__(self, *args, **kwargs): self.s = str(*args, **kwargs)
you can defer any methods you want to work on the underlying
self.s manually or automatically using
That being said, needing your own string type is something that should give you pause. There are many classes that should store a string as their main data, but you generally want to use
unicode (the latter if you’re representing text) for general representation of strings. (One common exception is if you have need to use a UI toolkit’s string type.) If you want to add functionality to your strings, try if you can to use functions that operate on strings rather than new objects to serve as strings, which keeps your code simpler and more compatible with everyone else’s programs.
When you instantiate a class, the arguments that you pass in, are passed to both the
__new__ (constructor) and then to the
__init__ (initializer) methods of the class. So if you inherit from a class that has restrictions on number of arguments that may be supplied during instantiation, you must guarantee that neither its
__new__, nor its
__init__ would get more arguments than they expect to get. So that is the problem that you have. You instantiate your class with
C("a", "B"). The interpreter looks for
__new__ method in
C doesn’t have it, so python peeps into its base class
str. And as it has one, that one is used and supplied with the both arguments. But
str.__new__ expects to get only one argument (besides its class object as the first argument). So
TypeError is raised. That is why you must extend it in your child class similarly to what you do with
__init__. But bear in mind that it must return class instance and that it is a static method (irrespective of whether it is defined with
@staticmethod decorator or not) that counts if you use
After carefully reading this, here is another attempt at subclassing str. The change from other answers is creating the instance in the correct class using
super(TitleText, cls).__new__ . This one seems to behave like a str whenever it’s used, but has allowed me to override a method:
class TitleText(str): title_text="" def __new__(cls,content,title_text): o=super(TitleText, cls).__new__(cls,content) o.title_text = title_text return o def title(self): return self.title_text >>> a=TitleText('name','A nice name') >>> a 'name' >>> a 'n' >>> a[0:2] 'na' >>> a.title() 'A nice name'
This lets you do slicing and subscripting correctly. What’s this for? For renaming the Django application in the admin index page.
The question was already answered above, this is just a tangential observation that may be useful to somebody.
I hit this question when trying to figure out a way to remove a temporary file after the dictionary it was being referred to goes deleted.
The context is a Flask session: the user can upload some files but give up before effectively commit the whole workflow it has to go through to get his/her data into the final destination. Until then, I keep the files in a temporary directory. Let’s say the user give up and closes the browser window, I don’t want those files lingering around.
Since I keep the temporary path in a Flask session — which is just a dictionary that eventually goes deleted (e.g, timeout), I can customize a
str class to hold the temporary directory address/path, and have its
__del__ method handling the temporary directory deletion.
Here it goes:
class Tempdir(str): def __new__(cls, *args, **kwargs): from tempfile import mkdtemp _dir = mkdtemp() return super().__new__(cls, _dir) def __del__(self): from shutil import rmtree rmtree(str(self))
Instantiate it in your python interpreter/app:
> d = Tempfile() > d '/var/folders/b1/frq3gywj3ljfqrf1yc7zk06r0000gn/T/tmptwa_g5fw' > > import os > os.path.exists(d) True
When you exit the interpreter:
$ ls /var/folders/b1/frq3gywj3ljfqrf1yc7zk06r0000gn/T/tmptwa_g5fw ls: /var/folders/b1/frq3gywj3ljfqrf1yc7zk06r0000gn/T/tmptwa_g5fw: No such file or directory
There you go.