Various instance behavior methods between Python 2.5 and 2.6

Trying to change the __unicode__ method to an instance after creating it creates different results in Python 2.5 and 2.6.

Here's the test script:

 class Dummy(object): def __unicode__(self): return u'one' def two(self): return u'two' d = Dummy() print unicode(d) d.__unicode__ = d.two print unicode(d) print d.__unicode__() 

In Python 2.5, this creates

 one two two 

That is, modifying the __unicode__ instance also modifies unicode(instance)

In Python 2.6, this produces

 one one two 

So, after changing unicode(instance) and instance.__unicode__() , different results are returned.

Why? How can I get this to work on Python 2.6?

(For what it's worth, the use case here is that I want to add something to the __unicode__ output for all subclasses of a given class, without changing the code of the subclasses.)

Edit to make use case a little understandable

I have a class A that has many subclasses. These subclasses define simple __unicode__ methods. I want to add logic so that for instances of a subclass of class A, the unicode (instance) gets something that was attached to the end. To keep the code simple, and because there are many subclasses that I don’t want to change, I would prefer to avoid editing the subclass code.

This is actually existing code that works in Python 2.5. This is something like this:

 class A(object): def __init__(self): self._original_unicode = self.__unicode__ self.__unicode__ = self.augmented_unicode def augmented_unicode(self): return self._original_unicode() + u' EXTRA' 

This is code that no longer works on 2.6. Any suggestions on how to achieve this without changing the subclass code? (If the answer involves metaclasses, note that class A itself is a subclass of another class - django.db.models.Model. - with a rather complex metaclass)

+4
source share
3 answers

It looks like you are not allowed monkey-patch protocol methods (those that start and end with double underscores):

Note

In practice, there is another exception that we did not manage here. Although you can override methods with instance attributes (correcting methods are very useful for monkeys for testing purposes) you cannot do this using Python protocol methods. These are magic methods whose names begin and end with double underscores. When called by the Python interpreter, they looked directly at the class and not at the instance (however, if you look at them directly - for example. X. Repr - the normal attribute search rules apply).

In this case, you may be stuck if you cannot go with ~ unutbu answer .

EDIT . Or you can use the __unicode__ base class __unicode__ for the dict instance object for the __unicode__ attribute. If it is present, then __unicode__ is defined in the instance object, and the class method calls the instance method. Otherwise, we return to the __unicode__ class __unicode__ .

I think this may allow your existing subclass to work without any change. However, it becomes ugly if the derived class wants to invoke the implementation of the class - you need to be careful to avoid endless loops. In this example, I did not implement such hacks; just commented on them.

 import types class Dummy(object): def __unicode__(self): func = self.__dict__.get("__unicode__", None) if func: // WARNING: if func() invokes this __unicode__ method directly, // an infinite loop could result. You may need an ugly hack to guard // against this. (Eg, set a flag on entry / unset the flag on exit, // using a try/finally to protect against exceptions.) return func() return u'one' def two(self): return u'two' d = Dummy() print unicode(d) funcType = type(Dummy.__unicode__) d.__unicode__ = types.MethodType(Dummy.two, d) print unicode(d) print d.__unicode__() 

Testing with Python 2.6 gives the following result:

 > python dummy.py one two two 
+2
source

Edit: In response to the OP comment: adding an indirect layer can allow you to change the unicode behavior for each instance:

 class Dummy(object): def __unicode__(self): return self._unicode() def _unicode(self): return u'one' def two(self): return u'two' d = Dummy() print unicode(d) # one d._unicode = d.two print unicode(d) # two print d.__unicode__() # two 
+2
source

Dan seems to be right about the logging methods for monkey patches, and that this was a change between Python 2.5 and Python 2.6.

My fix ended up making changes to classes, not instances:

 class A(object): def __init__(self): self.__class__.__unicode__ = self.__class__.augmented_unicode 
0
source

All Articles