47

Why is the first result False, should it not be True?

>>> from collections import OrderedDict
>>> OrderedDict.__repr__ is OrderedDict.__repr__
False
>>> dict.__repr__ is dict.__repr__
True
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Chameleon
  • 9,722
  • 16
  • 65
  • 127
  • 1
    Python 3.3 returns True – justengel Jun 23 '14 at 14:06
  • 1
    @JustinEngel: That's because Python 3 does away with unbound methods, all methods are bound. Try `OrderedDict().__repr__ is OrderedDict().__repr__` in Python 3 and you'll see the same behaviour. – Martijn Pieters Jun 23 '14 at 14:24
  • 1
    I curious how something like this would ever be touched on in practical code – Nick T Jun 23 '14 at 20:17
  • 1
    @NickT: Verifying a monkey-patched method would require knowledge of how methods are bound. Adding a method from one class to another class would also require that you extract the function object, not the method, etc. – Martijn Pieters Jun 24 '14 at 09:56

2 Answers2

56

For user-defined functions, in Python 2 unbound and bound methods are created on demand, through the descriptor protocol; OrderedDict.__repr__ is such a method object, as the wrapped function is implemented as a pure-Python function.

The descriptor protocol will call the __get__ method on objects that support it, so __repr__.__get__() is called whenever you try to access OrderedDict.__repr__; for classes None (no instance) and the class object itself are passed in. Because you get a new method object each time the function __get__ method is invoked, is fails. It is not the same method object.

dict.__repr__ is not a custom Python function but a C function, and its __get__ descriptor method essentially just returns self when accessed on the class. Accessing the attribute gives you the same object each time, so is works:

>>> dict.__repr__.__get__(None, dict) is dict.__repr__  # None means no instance
True

Methods have a __func__ attribute referencing the wrapped function, use that to test for identity:

>>> OrderedDict.__repr__
<unbound method OrderedDict.__repr__>
>>> OrderedDict.__repr__.__func__
<function __repr__ at 0x102c2f1b8>
>>> OrderedDict.__repr__.__func__.__get__(None, OrderedDict)
<unbound method OrderedDict.__repr__>
>>> OrderedDict.__repr__.__func__ is OrderedDict.__repr__.__func__
True

Python 3 does away with unbound methods, function.__get__(None, classobj) returns the function object itself (so it behaves like dict.__repr__ does). But you will see the same behaviour with bound methods, methods retrieved from an instance.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 1
    As a simply test to prove this for yourself, create a class with its ``__repr__`` method as ``__repr__ = lambda :x``, and the ``is`` test will fail. Actually, overriding ``__repr__`` in general will cause ``is`` to fail. – aruisdante Jun 23 '14 at 14:06
  • And, in the general case, NO classes' pure-python unbound methods compare as ``True`` with ``is``. – aruisdante Jun 23 '14 at 14:12
  • @aruisdante: well, you can create (more than one) reference to the method object, and then you can still use `is`. But retrieving a method from a class gives you a fresh object because the function `__get__` method produces a new object. – Martijn Pieters Jun 23 '14 at 14:13
  • Ah yeah, sure, I meant via direct comparison as the OP did. And obviously your answer actually says that, it just isn't immediately obvious. Perhaps providing a simple example where you show that for ANY class with ANY pure-python methods this will happen would be helpful to future viewers of this question. – aruisdante Jun 23 '14 at 14:15
  • @aruisdante: I am more focusing on the difference here with `dict.__repr__`; talking *just* about the method objects is a duplicate, actually. – Martijn Pieters Jun 23 '14 at 14:16
1

The two OrderedDict.__repr__ are not bound to the same object. If you try:

 OrderedDict.__repr__ == OrderedDict.__repr__

you'll see that they have the same value.

smesseim
  • 79
  • 6