使用 __getattr__ 覆盖魔法方法

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

我有一个类,它是成员的容器。所有成员都是同一类型。

class A(int):
    def __init__(self, n):
        super().__init__()
        self.n = n

    def do(self):
        print('adding 10')
        return self.n + 10


class B:
    def __init__(self, a1, a2):
        self.a = [a1, a2]

    def __getattr__(self, item):
        return getattr(self.a[0], item)


a1 = A(10)
a2 = A(5)
b = B(a1, a2)

__getattr__
覆盖
do
方法:

In[7]: b.do()
adding 10
Out[7]: 20

甚至在显式调用时覆盖

__add__

In[8]: b.__add__(1)
Out[8]: 11

但是

__add__
当被称为
+

时会失败
In[9]: b+1
TypeError: unsupported operand type(s) for +: 'B' and 'int'

如何重写魔术方法以按预期工作?

python python-3.x magic-methods
2个回答
2
投票

__getattr__
不处理运算符的原因在为什么 __getattr__ 能够处理 python 2.x 中的内置运算符重载?

在新式类中,特殊方法总是在类中查找(隐式查找)而不是实例中。

我的解决方法(并不完美,但允许使其工作)是在

B
中显式定义所需的 dunder 方法,并在它们上显式调用
__getattr__

class B:
    def __init__(self, a1, a2):
        self.a = [a1, a2]

    def __add__(self,other):
        return self.__getattr__("__add__")(other)

    def __getattr__(self, item):
        return getattr(self.a[0], item)

0
投票

想添加对Maximouse的评论的答案,以说明元类不能解决这个问题。给定:

class Meta(type):
    def __getattr__(self, name):
        return "meta-attr"

class X(metaclass=Meta):
    def __getattr__(self, name):
        return "attr"

然后:

>>> X().__add__(X())
"attr"
>>> X.__add__(X(), X())
"meta-attr"

但这失败了:

>>> X() + X()
TypeError: unsupported operand type(s) for +: 'Derived' and 'Derived'
    ...

这意味着这没有实现 OP 的目标。使用 dis,我们可以看到在底层 python 甚至从不进行属性查找,它只是调用

BINARY_ADD
:

>>> from dis import dis
>>> a, b = X(), X()
>>> dis("a + b")
  1           0 LOAD_NAME                0 (a)
              2 LOAD_NAME                1 (b)
              4 BINARY_ADD
              6 RETURN_VALUE

(在 python 3.8 上测试)

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