如何动态重写__setitem__?(无子类)

4 投票
2 回答
1584 浏览
提问于 2025-04-17 13:01

我在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派生的类。)

撰写回答