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

Sta*_*ean 11 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)
Run Code Online (Sandbox Code Playgroud)

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

有没有一种方法可以__getitem__在Python实例级别进行覆盖?

ruo*_*ola 7

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

对于自定义类,只有在对对象的类型(而不是在对象的实例字典中)定义的情况下,才能保证对特殊方法的隐式调用可以正常工作。

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


Oli*_*çon 5

别这样做...

项目查找协议将始终__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:
            return super().__getitem__(item)

foo = Foo()
foo.instance_getitem = lambda self, item: item + 1
print(foo[1]) # 2
Run Code Online (Sandbox Code Playgroud)