Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing __getitem__ in new-style classes

I have this code:

class A:
    def __init__(self):
        def method(self, item):
            print self, ": Getting item", item
        self.__getitem__ = types.MethodType(method, self, self.__class__)

class B(object):
    def __init__(self):
        def method(self, item):
            print self, ": Getting item", item
        self.__getitem__ = types.MethodType(method, self, self.__class__)

Then this works fine:

a = A()
a[0]

But this does not:

b = B()
b[0]

raising TypeError.

I found that new-style classes look for magic methods in class __dict__ instead of instance __dict__ . Is this right? Why is it so? Do you know about any article explaining the ideas behind? I tried RTFM, but maybe not the right ones or did not catch the thing...

Thank you very much! Paul

like image 213
Pavel Tomasko Avatar asked Dec 31 '12 13:12

Pavel Tomasko


1 Answers

This is documented in the Python datamodel documentation: Special method lookup for new-style classes:

For new-style classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.

and

The rationale behind this behaviour lies with a number of special methods such as __hash__() and __repr__() that are implemented by all objects, including type objects. If the implicit lookup of these methods used the conventional lookup process, they would fail when invoked on the type object itself[.]

So, because both hash(int) and hash(1) must work, special methods are looked up on the type instead of on the instance. If __hash__() was looked up straight on the object, hash(int) would be translated to int.__hash__(), and that would fail, because int.__hash__() is an unbound method and it expects to be called on an actual instance of int() (e.g. 1); so for hash(int), type.__hash__() should called instead:

>>> hash(1) == int.__hash__(1)
True
>>> hash(int) == type.__hash__(int)
True
>>> int.__hash__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor '__hash__' of 'int' object needs an argument

This is a backwards-incompatible change, so it only applies to new-style objects.

like image 96
Martijn Pieters Avatar answered Oct 11 '22 21:10

Martijn Pieters