2

I have a class, that is a container for members. all members are of the same type.

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)

the __getattr__ overrides the do method:

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

and even overrides __add__ when called explicitly

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

but __add__ fails when called as +

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

How can I override magic methods to work as expected?

DeanLa
  • 1,871
  • 3
  • 21
  • 37

1 Answers1

2

The reason why operators aren't handled by __getattr__ is covered in Why is __getattr__ capable of handling built-in operator overloads in python 2.x?

In new-style classes the special methods are always looked up in the class(implicit lookup) not instance.

My workaround (not perfect, but allows to make it work) would be to explicitly define the required dunder methods in B, and explicitly call __getattr__ on them:

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)
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • That is kind of what I did the first time, but in reality what I really need is a list of function names such as `names=['__add__','__sub__','__mul',...]` and then in the getattr I want to do `if name in names: getattr(self.a[0], name)` – DeanLa Jan 10 '20 at 15:02
  • I believe there should be a more elegant way than to explicitly define all of those. – DeanLa Jan 10 '20 at 15:05
  • 4
    @DeanLa You could create a metaclass and override `__getattr__` there. – Maximouse Jan 10 '20 at 15:06
  • 1
    yes, meta class is probably better that this cheap workaround – Jean-François Fabre Jan 10 '20 at 15:09