使用以下代码:
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__
?
不幸的是,令人惊讶的是,这是不允许的:
对于自定义类,特殊方法的隐式调用只是 如果在对象的类型上定义,而不是在 对象的实例字典。
来源:https://docs.python.org/3/reference/datamodel.html#special-lookup
项目查找协议将始终从类中恢复
__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
我最终不得不做一些愚蠢的事情,比如创建一个新对象,调用旧的
__getitem__
并做一些不同的事情:
class USLDatasetFromL2L(datasets.Dataset):
def __init__(self, original_l2l_dataset: datasets.Dataset):
self.original_l2l_dataset = original_l2l_dataset
self.transform = self.original_l2l_dataset.transform
self.original_l2l_dataset.target_transform = label_to_long
self.target_transform = self.original_l2l_dataset.target_transform
def __getitem__(self, index: int) -> tuple:
""" overwrite the getitem method for a l2l dataset. """
# - get the item
img, label = self.original_l2l_dataset[index]
# - transform the item only if the transform does exist and its not a tensor already
# img, label = self.original_l2l_dataset.x, self.original_l2l_dataset.y
if self.transform and not isinstance(img, Tensor):
img = self.transform(img)
if self.target_transform and not isinstance(label, Tensor):
label = self.target_transform(label)
return img, label
def __len__(self) -> int:
""" Get the length. """
return len(self.original_l2l_dataset)
您可以创建
__getitem__
一个描述符,在默认将类的原始方法绑定到实例之前,它首先尝试返回给定实例的同名属性:
class customizable:
def __init__(self, method):
self.method = method
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, obj_type=None):
if obj:
try:
return vars(obj)[self.name]
except KeyError:
return self.method.__get__(obj, obj_type)
return self.method
这样:
import types
class Foo():
@customizable
def __getitem__(self, x):
return x
def new_get(self, x):
return x + 1
x = Foo()
y = Foo()
x.__getitem__ = types.MethodType(new_get, x)
print(Foo.__getitem__)
print(x[42])
print(y[42])
输出:
<function Foo.__getitem__ at 0x154f7e55c310>
43
42