如何正确封装`__getitem__`方法以保持方括号功能

huangapple go评论133阅读模式
英文:

How to wrap __getitem__ method correctly to keep square brackets functionality

问题

我有一个类B它包装了类A的`__getitem__`方法

    def my_decorator(method):
        def wrapper(*args, **kwargs):
            print('Decorator')
            method(*args, **kwargs)
        return wrapper

    class A:
        def __getitem__(self, idx):
            print(idx)
    
    class B:
        def __init__(self):
            self._a = A()
            self._a.__getitem__ = my_decorator(self._a.__getitem__)
    
        def get_a(self, idx):
            self._a[idx]
    
    
    b = B()
    b.get_a(0)

我期望的输出应该是

    Decorator
    0
但是实际上我得到的输出是

    0
如果我将`get_a`方法改为如下所示我可以得到期望的行为

    def get_a(self, idx):
        self._a.__getitem__(idx)

似乎包装`__getitem__`方法并不影响使用方括号的访问方式有没有办法使原始代码在使用方括号访问时也能正常工作我不想修改`get_a`的实现方式
英文:

I have a class B that wraps the the __getitem__ method of class A:

def my_decorator(method):
    def wrapper(*args, **kwargs):
        print('Decorator')
        method(*args, **kwargs)
    return wrapper

class A:
    def __getitem__(self, idx):
        print(idx)

class B:
    def __init__(self):
        self._a = A()
        self._a.__getitem__ = my_decorator(self._a.__getitem__)

    def get_a(self, idx):
        self._a[idx]


b = B()
b.get_a(0)

I would expect the following output:

Decorator
0

But instead I just get:

0

If I change get_a method as follows, I get the expected behaviour:

def get_a(self, idx):
    self._a.__getitem__(idx)

It seems that wrapping the __getitem__ method doesn't affect the access with square brackets. Is there any way to make the original code work with square brackets access? I don't want to modify the implementation of get_a.

答案1

得分: 4

self._a[idx] 相当于 type(self._a).__getitem__(idx),而不是 self._a.__getitem__(idx)。你定义的实例属性 __getitem__[] 语法中会被忽略。

你需要定义一个继承自 A 的子类,重写 __getitem__ 方法。

class A:
    def __getitem__(self, idx):
        print(idx)

class SubA(A):
    def __getitem__(self, idx):
        print('Decorator')
        return super().__getitem__(idx)

class B:
    def __init__(self):
        self._a = SubA()

    def get_a(self, idx):
        self._a[idx]

b = B()
b.get_a(0)

(请注意:__getitem__ 应该真正返回一个值,这样的话 get_a 需要显式地返回 self._a[idx] 的值。)

英文:

self._a[idx] is equivalent to type(self._a).__getitem__(idx), not self._a.__getitem__(idx). The instance attribute __getitem__ you define is ignored by the [] syntax.

You need to define a subclass of A that overrides __getitem__.

class A:
    def __getitem__(self, idx):
        print(idx)


class SubA(A):
    def __getitem__(self, idx):
        print('"Decorator"')
        return super().__getitem__(idx)


class B:
    def __init__(self):
        self._a = SubA()

    def get_a(self, idx):
        self._a[idx]


b = B()
b.get_a(0)

(Just to note: __getitem__ should really return a value, in which case get_a needs to explicitly return the value of self._a[idx].)

huangapple
  • 本文由 发表于 2023年3月15日 19:47:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/75744275.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定