如何动态重写__setitem__?(无子类)
我在Python2.7中无法重写一些内置函数,比如'__setitem__'(我测试过的早期版本也是这样)。
虽然我知道通过子类化可以很容易做到这一点,但这不是我想要的,我需要能够动态地重写这些方法。
显然,当我的类是'object'的子类时,重写的方法总是会调用原来的方法,但当我的类不是'object'时,就可以正常工作:
>>> 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
出于某种原因,似乎对象在处理这些内置函数时使用了不同的方法解析顺序。
这是个bug吗?我是不是做错了什么?
2 个回答
2
正如Sven Marnach所说,问题在于对于新式类,使用索引赋值时会调用类里面定义的__setitem__
。不过,解决这个问题并不难。解决方案通常涉及到另一个层次的间接性。这虽然不是一个很好的设计,但看起来是实现你想要的功能的明显方法:
>>> 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
5
对于旧式类,每次需要特殊方法时,都会在实例上查找这些方法。而新式类只会在实例的类型上查找特殊方法,而不是在实例本身的字典里查找——这就是你看到的行为原因。
(如果你不知道的话,新式类是指直接或间接从object
派生的类。)