campos.ddc
campos.ddc

Reputation: 771

How to dynamically override __setitem__? (no subclass)

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

Answers (2)

senderle
senderle

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

Sven Marnach
Sven Marnach

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

Related Questions