Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does `instance_of_object.foo is instance_of_object.foo` evaluate False? [duplicate]

If I have a

class A:
  def foo(self):
    pass

this evaluates to True:

getattr(A, 'foo') is A.foo

but this evaluates to False:

a = A()
getattr(a, 'foo') is a.foo

as does

a.foo is a.foo

Why?

I found that getattr(a, 'foo') and a.foo both are represented by

<bound method A.foo of <__main__.A object at 0x7a2c4de10d50>>)

So no hint there....

like image 439
steffen Avatar asked May 23 '18 23:05

steffen


2 Answers

At least in CPython, bound methods are implemented as an instance of a class method. Every time you ask for the value of a bound function, you get a new instance of this class.

x = a.foo
y = a.foo

assert x is not y
id(x)  # E.g. 139664144271304
id(y)  # E.g. 139664144033992
type(x)  # <class 'method'>
type(y)  # <class 'method'>

All this class does is store a reference to the instance and the unbound function, and when you call the class it calls the unbound function with the stored instance (along with your other arguments).

Unbound functions, like A.foo, are just regular old functions - no new instances of proxy classes are being constructed, so identity works as you expect.

The reason for this difference is that the semantic meaning of a.foo depends on two things, the value of a and the value of A.foo. In order to be able to get this meaning at any point in time later, both of these values need to be stored. This is what the method class does.

Conversely, the meaning of A.foo depends only on a single value: A.foo. So no additional work is required to store anything, and the value itself is used.

You might consider the idea of pre-allocating bound method instances, so that a.foo always returns the same immutable object - but given the dynamic nature of Python, it is simpler and cheaper to just construct a new one each time, even if they could be the same.

like image 70
GManNickG Avatar answered Nov 14 '22 21:11

GManNickG


To add to @GManNickG answer:

getattr(a, 'foo').__func__ is a.foo.__func__ 

will return True.

like image 33
AGN Gazer Avatar answered Nov 14 '22 21:11

AGN Gazer