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.