是否可以在Python实例级别覆盖__getitem__?

时间:2019-08-08 13:10:21

标签: python monkeypatching

使用以下代码:

import types

class Foo():
    def __getitem__(self, x):
        return x

def new_get(self, x):
    return x + 1

x = Foo()
x.__getitem__ = types.MethodType(new_get, x)

x.__getitem__(42)将返回43,但是x[42]将返回42。

是否可以在Python实例级别覆盖__getitem__

2 个答案:

答案 0 :(得分:8)

不幸的是,非常令人惊讶的是,这是不允许的:

  

对于自定义类,仅对特殊方法进行隐式调用   如果定义在对象的类型上,而不是在   对象的实例字典。

来源:https://docs.python.org/3/reference/datamodel.html#special-lookup

答案 1 :(得分:1)

不要这样做...

项目查找协议将始终从类中恢复__getitem__,甚至不会查看实例__dict__。通常,这实际上是一件好事,因为否则将允许同一类的实例在概念上彼此不同,这与类背后的整个思想背道而驰。

但是...

尽管如此,在某些情况下这可能会有所帮助,例如在为测试目的进行猴子修补时。

由于直接在类级别上搜索dunder,因此项查找逻辑也必须在类级别上更新。

因此,一种解决方案是更新__getitem__,以便它首先在实例__dict__中查找实例级功能。

在此示例中,我们将dict子类化以允许实例级__getitem__

class Foo(dict):
    def __getitem__(self, item):
        if "instance_getitem" in self.__dict__:
            return self.instance_getitem(self, item)
        else:
            super().__getitem__(item)

foo = Foo()
foo.instance_getitem = lambda self, item: item + 1
print(foo[1]) # 2