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



使用以下代码:

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)

最新更新