5

I have the following class:

class MyInt:
    def __init__(self, v):
        if type(v) != int:
            raise ValueError('value must be an int')
        self.v = v

    def __getattr__(self, attr):
        return getattr(self.v, attr)

i = MyInt(0)
print(i + 1)

I get the error: TypeError: unsupported operand type(s) for +: 'MyInt' and 'int'

Shouldn't i.__add__(1) be called? And shouldn't __getattr__ be called when no such method is found in the MyInt class?

martineau
  • 119,623
  • 25
  • 170
  • 301
Jon McClung
  • 1,619
  • 20
  • 28
  • 2
    [In addition to bypassing any instance attributes in the interest of correctness, implicit special method lookup generally also bypasses the `__getattribute__()` method even of the object’s metaclass](https://docs.python.org/3/reference/datamodel.html#special-method-lookup) – Ashwini Chaudhary Jul 10 '16 at 18:06

1 Answers1

9

__getattr__ cannot be used to generate other magic methods. You'll need to implement all of them individually.

When the Python language internals look up magic methods like __add__, they completely bypass __getattr__, __getattribute__, and the instance dict. The lookup goes roughly like

def find_magic_method(object, method_name):
    for klass in type(object).__mro__:
        if method_name in klass.__dict__:
            return klass.__dict__[method_name]
    raise AttributeError

If you want to see the exact lookup procedure, it's _PyObject_LookupSpecial in Objects/typeobject.c.

If you're wondering why Python does this, there are a lot of magic methods for which it would be really awkward or impossible to do what you were expecting. For example, Python couldn't possibly use __getattribute__ to look up __getattribute__, as that would cause infinite recursion with no base case.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • 2
    The difference between the real lookup and that python code is that the above python code could be manipulated using meta-classes to provide a custom `__dict__`, while the real lookup bypasses even the metaclass and accesses the `__dict__` mapping via the direct field in the underlying C structure. – Bakuriu Jul 10 '16 at 18:35
  • That's a long file, could you give me a line number or function name please? – Jon McClung Jul 11 '16 at 00:00
  • 1
    @JonMcClung: The link goes right to the function, and I did say the function name is `_PyObject_LookupSpecial `. Most of the helpers it relies on are in the same file, so you should be able to find them with Ctrl-F. – user2357112 Jul 11 '16 at 00:22
  • My bad. I scrolled around before it finished loading, causing it to end up in the wrong place. – Jon McClung Jul 11 '16 at 00:23