I am trying to create a custom object that passes all non-existent method calls down to a member attribute. This works under normal custom method invocations, but fails when attempting to call arithmetic operators.
Below is a console snippet of an example class, a test function, and a cleaned up disassembly of the test function.
>>> class NoAdd(object):
... member = 0
... def __getattr__(self, item):
... print('NoAdd __getattr__')
... # return getattr(self.member, item)
... if item == '__add__':
... return self.member.__add__
>>> def test():
... print('Call __add__ directly.')
... NoAdd().__add__(5) # 5
... print('Implicit __add__.')
... NoAdd() + 5 # TypeError
>>> dis(test)
3 8 LOAD_GLOBAL 1 (NoAdd)
10 CALL_FUNCTION 0
12 LOAD_ATTR 2 (__add__)
14 LOAD_CONST 2 (5)
16 CALL_FUNCTION 1
18 POP_TOP
5 28 LOAD_GLOBAL 1 (NoAdd)
30 CALL_FUNCTION 0
32 LOAD_CONST 2 (5)
34 BINARY_ADD
36 POP_TOP
38 LOAD_CONST 0 (None)
40 RETURN_VALUE
>>> test()
Call __add__ directly.
NoAdd __getattr__
Implicit __add__.
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 5, in test
TypeError: unsupported operand type(s) for +: 'NoAdd' and 'int'
I thought that the Python interpreter would look for the __add__
method using the standard procedure of invoking __getattr__
when the method was not found in the object's method list, before looking for __radd__
in the int. This is apparently not what is happening.
Can someone help me out by explaining what is going on or helping me find out where in the Python source code I can find what BINARY_ADD
is doing? I'm not sure if I can fix this, but a workaround would be appreciated.