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 __init__? why?
>>> 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'>)
Since __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 cls
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'
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. 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 super function.
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 str self.s manually or automatically using __getattr__.
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 str or 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.
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[0]
'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.
Use __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
print returns hello.
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.
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.
Related
Is there a way to circumvent the constructor __init__ of a class in python?
Example:
class A(object):
def __init__(self):
print "FAILURE"
def Print(self):
print "YEHAA"
Now I would like to create an instance of A. It could look like this, however this syntax is not correct.
a = A
a.Print()
EDIT:
An even more complex example:
Suppose I have an object C, which purpose it is to store one single parameter and do some computations with it. The parameter, however, is not passed as such but it is embedded in a huge parameter file. It could look something like this:
class C(object):
def __init__(self, ParameterFile):
self._Parameter = self._ExtractParamterFile(ParameterFile)
def _ExtractParamterFile(self, ParameterFile):
#does some complex magic to extract the right parameter
return the_extracted_parameter
Now I would like to dump and load an instance of that object C. However, when I load this object, I only have the single variable self._Parameter and I cannot call the constructor, because it is expecting the parameter file.
#staticmethod
def Load(file):
f = open(file, "rb")
oldObject = pickle.load(f)
f.close()
#somehow create newObject without calling __init__
newObject._Parameter = oldObject._Parameter
return newObject
In other words, it is not possible to create an instance without passing the parameter file. In my "real" case, however, it is not a parameter file but some huge junk of data I certainly not want to carry around in memory or even store it to disc.
And since I want to return an instance of C from the method Load I do somehow have to call the constructor.
OLD EDIT:
A more complex example, which explains why I am asking the question:
class B(object):
def __init__(self, name, data):
self._Name = name
#do something with data, but do NOT save data in a variable
#staticmethod
def Load(self, file, newName):
f = open(file, "rb")
s = pickle.load(f)
f.close()
newS = B(???)
newS._Name = newName
return newS
As you can see, since data is not stored in a class variable I cannot pass it to __init__. Of course I could simply store it, but what if the data is a huge object, which I do not want to carry around in memory all the time or even save it to disc?
You can circumvent __init__ by calling __new__ directly. Then you can create a object of the given type and call an alternative method for __init__. This is something that pickle would do.
However, first I'd like to stress very much that it is something that you shouldn't do and whatever you're trying to achieve, there are better ways to do it, some of which have been mentioned in the other answers. In particular, it's a bad idea to skip calling __init__.
When objects are created, more or less this happens:
a = A.__new__(A, *args, **kwargs)
a.__init__(*args, **kwargs)
You could skip the second step.
Here's why you shouldn't do this: The purpose of __init__ is to initialize the object, fill in all the fields and ensure that the __init__ methods of the parent classes are also called. With pickle it is an exception because it tries to store all the data associated with the object (including any fields/instance variables that are set for the object), and so anything that was set by __init__ the previous time would be restored by pickle, there's no need to call it again.
If you skip __init__ and use an alternative initializer, you'd have a sort of a code duplication - there would be two places where the instance variables are filled in, and it's easy to miss one of them in one of the initializers or accidentally make the two fill the fields act differently. This gives the possibility of subtle bugs that aren't that trivial to trace (you'd have to know which initializer was called), and the code will be more difficult to maintain. Not to mention that you'd be in an even bigger mess if you're using inheritance - the problems will go up the inheritance chain, because you'd have to use this alternative initializer everywhere up the chain.
Also by doing so you'd be more or less overriding Python's instance creation and making your own. Python already does that for you pretty well, no need to go reinventing it and it will confuse people using your code.
Here's what to best do instead: Use a single __init__ method that is to be called for all possible instantiations of the class that initializes all instance variables properly. For different modes of initialization use either of the two approaches:
Support different signatures for __init__ that handle your cases by using optional arguments.
Create several class methods that serve as alternative constructors. Make sure they all create instances of the class in the normal way (i.e. calling __init__), as shown by Roman Bodnarchuk, while performing additional work or whatever. It's best if they pass all the data to the class (and __init__ handles it), but if that's impossible or inconvenient, you can set some instance variables after the instance was created and __init__ is done initializing.
If __init__ has an optional step (e.g. like processing that data argument, although you'd have to be more specific), you can either make it an optional argument or make a normal method that does the processing... or both.
Use classmethod decorator for your Load method:
class B(object):
def __init__(self, name, data):
self._Name = name
#store data
#classmethod
def Load(cls, file, newName):
f = open(file, "rb")
s = pickle.load(f)
f.close()
return cls(newName, s)
So you can do:
loaded_obj = B.Load('filename.txt', 'foo')
Edit:
Anyway, if you still want to omit __init__ method, try __new__:
>>> class A(object):
... def __init__(self):
... print '__init__'
...
>>> A()
__init__
<__main__.A object at 0x800f1f710>
>>> a = A.__new__(A)
>>> a
<__main__.A object at 0x800f1fd50>
Taking your question literally I would use meta classes :
class MetaSkipInit(type):
def __call__(cls):
return cls.__new__(cls)
class B(object):
__metaclass__ = MetaSkipInit
def __init__(self):
print "FAILURE"
def Print(self):
print "YEHAA"
b = B()
b.Print()
This can be useful e.g. for copying constructors without polluting the parameter list.
But to do this properly would be more work and care than my proposed hack.
Not really. The purpose of __init__ is to instantiate an object, and by default it really doesn't do anything. If the __init__ method is not doing what you want, and it's not your own code to change, you can choose to switch it out though. For example, taking your class A, we could do the following to avoid calling that __init__ method:
def emptyinit(self):
pass
A.__init__ = emptyinit
a = A()
a.Print()
This will dynamically switch out which __init__ method from the class, replacing it with an empty call. Note that this is probably NOT a good thing to do, as it does not call the super class's __init__ method.
You could also subclass it to create your own class that does everything the same, except overriding the __init__ method to do what you want it to (perhaps nothing).
Perhaps, however, you simply wish to call the method from the class without instantiating an object. If that is the case, you should look into the #classmethod and #staticmethod decorators. They allow for just that type of behavior.
In your code you have put the #staticmethod decorator, which does not take a self argument. Perhaps what may be better for the purpose would a #classmethod, which might look more like this:
#classmethod
def Load(cls, file, newName):
# Get the data
data = getdata()
# Create an instance of B with the data
return cls.B(newName, data)
UPDATE: Rosh's Excellent answer pointed out that you CAN avoid calling __init__ by implementing __new__, which I was actually unaware of (although it makes perfect sense). Thanks Rosh!
I was reading the Python cookbook and there's a section talking about this: the example is given using __new__ to bypass __init__()
>>> class A:
def __init__(self,a):
self.a = a
>>> test = A('a')
>>> test.a
'a'
>>> test_noinit = A.__new__(A)
>>> test_noinit.a
Traceback (most recent call last):
File "", line 1, in
test_noinit.a
AttributeError: 'A' object has no attribute 'a'
>>>
However I think this only works in Python3. Below is running under 2.7
>>> class A:
def __init__(self,a):
self.a = a
>>> test = A.__new__(A)
Traceback (most recent call last):
File "", line 1, in
test = A.__new__(A)
AttributeError: class A has no attribute '__new__'
>>>
As I said in my comment you could change your __init__ method so that it allows creation without giving any values to its parameters:
def __init__(self, p0, p1, p2):
# some logic
would become:
def __init__(self, p0=None, p1=None, p2=None):
if p0 and p1 and p2:
# some logic
or:
def __init__(self, p0=None, p1=None, p2=None, init=True):
if init:
# some logic
I wrote a class that can handle integers with arbitrary precision (just for learning purposes). The class takes a string representation of an integer and converts it into an instance of BigInt for further calculations.
Often times you need the numbers Zero and One, so I thought it would be helpfull if the class could return these. I tried the following:
class BigInt():
zero = BigInt("0")
def __init__(self, value):
####yada-yada####
This doesn't work. Error: "name 'BigInt' is not defined"
Then I tried the following:
class BigInt():
__zero = None
#staticmethod
def zero():
if BigInt.__zero is None:
BigInt.__zero = BigInt('0')
return BigInt.__zero
def __init__(self, value):
####yada-yada####
This actually works very well. What I don't like is that zero is a method (and thus has to be called with BigInt.zero()) which is counterintuitive since it should just refer to a fixed value.
So I tried changing zero to become a property, but then writing BigInt.zero returns an instance of the class property instead of BigInt because of the decorator used. That instance cannot be used for calculations because of the wrong type.
Is there a way around this issue?
A static property...? We call a static property an "attribute". This is not Java, Python is a dynamically typed language and such a construct would be really overcomplicating matters.
Just do this, setting a class attribute:
class BigInt:
def __init__(self, value):
...
BigInt.zero = BigInt("0")
If you want it to be entirely defined within the class, do it using a decorator (but be aware it's just a more fancy way of writing the same thing).
def add_zero(cls):
cls.zero = cls("0")
return cls
#add_zero
class BigInt:
...
The question is contradictory: static and property don't go together in this way. Static attributes in Python are simply ones that are only assigned once, and the language itself includes a very large number of these. (Most strings are interred, all integers < a certain value are pre-constructed, etc. E.g. the string module.). Easiest approach is to statically assign the attributes after construction as wim illustrates:
class Foo:
...
Foo.first = Foo()
...
Or, as he further suggested, using a class decorator to perform the assignments, which is functionally the same as the above. A decorator is, effectively, a function that is given the "decorated" function as an argument, and must return a function to effectively replace the original one. This may be the original function, say, modified with some annotations, or may be an entirely different function. The original (decorated) function may or may not be called as appropriate for the decorator.
def preload(**values):
def inner(cls):
for k, v in values.items():
setattr(cls, k, cls(v))
return cls
return inner
This can then be used dynamically:
#preload(zero=0, one=1)
class Foo:
...
If the purpose is to save some time on common integer values, a defaultdict mapping integers to constructed BigInts could be useful as a form of caching and streamlined construction / singleton storage. (E.g. BigInt.numbers[27])
However, the problem of utilizing #property at the class level intrigued me, so I did some digging. It is entirely possible to make use of "descriptor protocol objects" (which the #property decorator returns) at the class level if you punt the attribute up the object model hierarchy, to the metaclass.
class Foo(type):
#property
def bar(cls):
print("I'm a", cls)
return 27
class Bar(metaclass=Foo):
...
>>> Bar.bar
I'm a <class '__main__.Bar'>
<<< 27
Notably, this attribute is not accessible from instances:
>>> Bar().bar
AttributeError: 'Bar' object has no attribute 'bar'
Hope this helps!
I am learning Python and so far I can tell the things below about __new__ and __init__:
__new__ is for object creation
__init__ is for object initialization
__new__ is invoked before __init__ as __new__ returns a new instance and __init__ invoked afterwards to initialize inner state.
__new__ is good for immutable object as they cannot be changed once they are assigned. So we can return new instance which has new state.
We can use __new__ and __init__ for both mutable object as its inner state can be changed.
But I have another questions now.
When I create a new instance such as a = MyClass("hello","world"), how these arguments are passed? I mean how I should structure the class using __init__ and __new__ as they are different and both accepts arbitrary arguments besides default first argument.
self keyword is in terms of name can be changed to something else? But I am wondering cls is in terms of name is subject to change to something else as it is just a parameter name?
I made a little experiments as such below:
>>> class MyClass(tuple):
def __new__(tuple):
return [1,2,3]
and I did below:
>>> a = MyClass()
>>> a
[1, 2, 3]
Albeit I said I want to return tuple, this code works fine and returned me [1,2,3]. I knew we were passing the first parameters as the type we wanted to receive once the __new__ function is invoked. We are talking about New function right? I don't know other languages return type other than bound type?
And I did anther things as well:
>>> issubclass(MyClass,list)
False
>>> issubclass(MyClass,tuple)
True
>>> isinstance(a,MyClass)
False
>>> isinstance(a,tuple)
False
>>> isinstance(a,list)
True
I didn't do more experiment because the further wasn't bright and I decided to stop there and decided to ask StackOverflow.
The SO posts I read:
Python object creation
Python's use of __new__ and __init__?
how I should structure the class using __init__ and __new__ as they are different and both accepts arbitrary arguments besides default first argument.
Only rarely will you have to worry about __new__. Usually, you'll just define __init__ and let the default __new__ pass the constructor arguments to it.
self keyword is in terms of name can be changed to something else? But I am wondering cls is in terms of name is subject to change to something else as it is just a parameter name?
Both are just parameter names with no special meaning in the language. But their use is a very strong convention in the Python community; most Pythonistas will never change the names self and cls in these contexts and will be confused when someone else does.
Note that your use of def __new__(tuple) re-binds the name tuple inside the constructor function. When actually implementing __new__, you'll want to do it as
def __new__(cls, *args, **kwargs):
# do allocation to get an object, say, obj
return obj
Albeit I said I want to return tuple, this code works fine and returned me [1,2,3].
MyClass() will have the value that __new__ returns. There's no implicit type checking in Python; it's the responsibility of the programmer to return the correct type ("we're all consenting adults here"). Being able to return a different type than requested can be useful for implementing factories: you can return a subclass of the type requested.
This also explains the issubclass/isinstance behavior you observe: the subclass relationship follows from your use of class MyClass(tuple), the isinstance reflects that you return the "wrong" type from __new__.
For reference, check out the requirements for __new__ in the Python Language Reference.
Edit: ok, here's an example of potentially useful use of __new__. The class Eel keeps track of how many eels are alive in the process and refuses to allocate if this exceeds some maximum.
class Eel(object):
MAX_EELS = 20
n_eels = 0
def __new__(cls, *args, **kwargs):
if cls.n_eels == cls.MAX_EELS:
raise HovercraftFull()
obj = super(Eel, cls).__new__(cls)
cls.n_eels += 1
return obj
def __init__(self, voltage):
self.voltage = voltage
def __del__(self):
type(self).n_eels -= 1
def electric(self):
"""Is this an electric eel?"""
return self.voltage > 0
Mind you, there are smarter ways to accomplish this behavior.
I recently developed a class named DocumentWrapper around some ORM document object in Python to transparently add some features to it without changing its interface in any way.
I just have one issue with this. Let's say I have some User object wrapped in it. Calling isinstance(some_var, User) will return False because some_var indeed is an instance of DocumentWrapper.
Is there any way to fake the type of an object in Python to have the same call return True?
You can use the __instancecheck__ magic method to override the default isinstance behaviour:
#classmethod
def __instancecheck__(cls, instance):
return isinstance(instance, User)
This is only if you want your object to be a transparent wrapper; that is, if you want a DocumentWrapper to behave like a User. Otherwise, just expose the wrapped class as an attribute.
This is a Python 3 addition; it came with abstract base classes. You can't do the same in Python 2.
Override __class__ in your wrapper class DocumentWrapper:
class DocumentWrapper(object):
#property
def __class__(self):
return User
>>> isinstance(DocumentWrapper(), User)
True
This way no modifications to the wrapped class User are needed.
Python Mock does the same (see mock.py:612 in mock-2.0.0, couldn't find sources online to link to, sorry).
Testing the type of an object is usually an antipattern in python. In some cases it makes sense to test the "duck type" of the object, something like:
hasattr(some_var, "username")
But even that's undesirable, for instance there are reasons why that expression might return false, even though a wrapper uses some magic with __getattribute__ to correctly proxy the attribute.
It's usually preferred to allow variables only take a single abstract type, and possibly None. Different behaviours based on different inputs should be achieved by passing the optionally typed data in different variables. You want to do something like this:
def dosomething(some_user=None, some_otherthing=None):
if some_user is not None:
#do the "User" type action
elif some_otherthing is not None:
#etc...
else:
raise ValueError("not enough arguments")
Of course, this all assumes you have some level of control of the code that is doing the type checking. Suppose it isn't. for "isinstance()" to return true, the class must appear in the instance's bases, or the class must have an __instancecheck__. Since you don't control either of those things for the class, you have to resort to some shenanigans on the instance. Do something like this:
def wrap_user(instance):
class wrapped_user(type(instance)):
__metaclass__ = type
def __init__(self):
pass
def __getattribute__(self, attr):
self_dict = object.__getattribute__(type(self), '__dict__')
if attr in self_dict:
return self_dict[attr]
return getattr(instance, attr)
def extra_feature(self, foo):
return instance.username + foo # or whatever
return wrapped_user()
What we're doing is creating a new class dynamically at the time we need to wrap the instance, and actually inherit from the wrapped object's __class__. We also go to the extra trouble of overriding the __metaclass__, in case the original had some extra behaviors we don't actually want to encounter (like looking for a database table with a certain class name). A nice convenience of this style is that we never have to create any instance attributes on the wrapper class, there is no self.wrapped_object, since that value is present at class creation time.
Edit: As pointed out in comments, the above only works for some simple types, if you need to proxy more elaborate attributes on the target object, (say, methods), then see the following answer: Python - Faking Type Continued
Here is a solution by using metaclass, but you need to modify the wrapped classes:
>>> class DocumentWrapper:
def __init__(self, wrapped_obj):
self.wrapped_obj = wrapped_obj
>>> class MetaWrapper(abc.ABCMeta):
def __instancecheck__(self, instance):
try:
return isinstance(instance.wrapped_obj, self)
except:
return isinstance(instance, self)
>>> class User(metaclass=MetaWrapper):
pass
>>> user=DocumentWrapper(User())
>>> isinstance(user,User)
True
>>> class User2:
pass
>>> user2=DocumentWrapper(User2())
>>> isinstance(user2,User2)
False
It sounds like you want to test the type of the object your DocumentWrapper wraps, not the type of the DocumentWrapper itself. If that's right, then the interface to DocumentWrapper needs to expose that type. You might add a method to your DocumentWrapper class that returns the type of the wrapped object, for instance. But I don't think that making the call to isinstance ambiguous, by making it return True when it's not, is the right way to solve this.
The best way is to inherit DocumentWrapper from the User itself, or mix-in pattern and doing multiple inherintance from many classes
class DocumentWrapper(User, object)
You can also fake isinstance() results by manipulating obj.__class__ but this is deep level magic and should not be done.
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 __init__? why?
>>> 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'>)
Since __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 cls
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'
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. 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 super function.
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 str self.s manually or automatically using __getattr__.
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 str or 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.
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[0]
'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.
Use __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
print returns hello.
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.
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.