如何动态覆盖__setitem__? (没有子类)

时间:2012-02-14 17:30:15

标签: python override python-2.7 built-in

我无法覆盖 Python2.7 中的某些内置函数,例如'__setitem__'(尽管在我测试的先前版本中也是如此)

虽然我知道通过子类化很容易做到这一点,但这不是我想要的,我需要能够动态覆盖这些方法。

显然,当我的班级是“对象”的子类时,被覆盖的方法总是会调用原始方法,但是当我的班级不是“对象”时',它有效:

>>> 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

出于某种原因,对象似乎对这些内置函数使用了一些不同的方法解析顺序。

这是一个错误吗?我做错了吗?

2 个答案:

答案 0 :(得分:5)

对于旧式类,每次需要时都会在实例上查找特殊方法。新式类只在实例的类型上查找特殊方法,而不是在实例的字典本身中查找特殊方法 - 这就是您看到所看到的行为的原因。

(如果您不知道 - 新式类是直接或间接派生自object的类。)

答案 1 :(得分:2)

正如Sven Marnach所述,问题在于对于新式类,索引分配使用定义的__setitem__。但要解决这个问题并不难。通常情况下,解决方案涉及another level of indirection。这不是一个很棒的设计,但它似乎是做你想要的显而易见的方式:

>>> 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