是否可以在Python中的实例级别重写__getitem__?

问题描述 投票:0回答:4

使用以下代码:

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__

python monkeypatching
4个回答
11
投票

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

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

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


4
投票

别这样做...

项目查找协议将始终从类中恢复

__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

1
投票

我最终不得不做一些愚蠢的事情,比如创建一个新对象,调用旧的

__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)

0
投票

您可以创建

__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

演示:https://ideone.com/fLjlWD

© www.soinside.com 2019 - 2024. All rights reserved.