Reputation: 771
I'm not able to override some builtin functions, such as '__setitem__', in Python2.7 (although the same occurs in previous versions I tested)
Although I know this is easy to do via subclassing, this is not what I want here, I need to be able to dynamically override these methods.
Apparently, when my class is a subclass of 'object', the overridden method always ends up calling the original one, but when my class is not an 'object', it works:
>>> def new_implementation(k, v):
... print 'New implementation'
...
### class that extends object
>>> class ExtendsObject(object):
... def __setitem__(self, k, v):
... print 'ExtendsObject implementation'
...
### Checking implementation
>>> obj = ExtendsObject()
>>> obj[0]=0
ExtendsObject implementation
### trying to override setitem, no success
>>> obj.__setitem__ = new_implementation
>>> obj[0]=0
ExtendsObject implementation
### class that does NOT extends object
>>> class DoesNotExtend:
... def __setitem__(self, k, v):
... print 'DoesNotExtend implementation'
...
### Checking implementation
>>> obj_2 = DoesNotExtend()
>>> obj_2[0]=0
DoesNotExtend implementation
### overriding now works!
>>> obj_2.__setitem__ = new_implementation
>>> obj_2[0]=0
New implementation
For some reason, it seems that objects use some different method resolution order for these built-in functions.
Is this a bug? Am I doing something wrong?
Upvotes: 4
Views: 1602
Reputation: 150947
As Sven Marnach says, the problem is that for new-style classes, indexed assignment uses the __setitem__
defined by the class. But it's not hard to get around this problem. The solution, as is so often the case, involves another level of indirection. This isn't a great design, but it seems like the obvious way to do what you want:
>>> class Foo(object):
... def __setitem__(self, k, v):
... return self._instance_setitem(k, v)
... def _instance_setitem(self, k, v):
... print 'old setitem'
...
>>> def new_setitem(self, k, v):
... print 'new setitem'
...
>>> f[0] = 0
old setitem
>>> f._instance_setitem = types.MethodType(new_setitem, f, Foo)
>>> f[0] = 0
new setitem
Upvotes: 2
Reputation: 601351
For old-style classes, special methods were looked up on the instance each time they were needed. New-style classes only look up special methods on the type of the instance, not in the instance's dictionary itself -- that's why you see the behaviour you see.
(In case you don't know -- a new-style class is a class directly or indirectly derived from object
.)
Upvotes: 5