2

I've noticed that sometimes instance methods do not compare as identical (using is rather than ==), even when they relate to the same bound instance method e.g.

>>> class A(object):
...     def f(self):
...             print "hi"
... 
>>> a = A()
>>> f1 = a.f
>>> f2 = a.f
>>> f1 is f2
False
>>> f1 == f2
True

I've ended up using == to check to see if two variables refer to the same bound method, but I was wondering if anyone knew why is does not behave as I would expect?

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • 1
    @chishaku: No, that's not a duplicate. This question isn't due to a misunderstanding of `is`, but a misunderstanding of how instance methods are created. – user2357112 Feb 19 '16 at 21:07
  • 1
    @chishaku I disagree - it is not a duplicate of that question. There is a specific semantic behind bound methods that needs to be explained here - it's not self-evident by understanding the difference between is and == – deets Feb 19 '16 at 21:07
  • Not an answer but `id`s are different : http://pastebin.com/Yzv7G6Su . – SylvainD Feb 19 '16 at 21:08

1 Answers1

2

methods are implemented as descriptors -- So each time you access the f member, a new function is created. You can see this by looking at their ids...

>>> class A(object):
...     def f(self):
...         pass
... 
>>> a = A()
>>> f1 = a.f
>>> f2 = a.f
>>> id(f1)
4325305232
>>> id(f2)
4325818528

To be a little more clear what I mean when I say that they are implemented via descriptors, the following expressions:

a = A()
func = a.f

are equivalent to:

a = A()
func = A.f.__get__(a, A)

Clearly you don't want to be writing the latter all the time, so the shortcut is pretty nice :-).

With that said, this starts to explain how a bound method knows what self is since a (which is self in the method) gets passed to __get__ which is what constructs the bound method.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • I would add that == is equality testing... where as IS is testing the identity of it – Joe Swindell Feb 19 '16 at 21:11
  • At the interactive prompt if I type `id(a.f)` I continually get back the same id. But once I assign the result to two different names I get different ids. This confuses me. What's special about assignment? I thought I understood Python names and values. – Steven Rumbalski Feb 19 '16 at 21:13
  • 2
    @StevenRumbalski: [That's just a quirk of CPython's reference-counting garbage collection.](http://stackoverflow.com/questions/20753364/why-is-the-id-of-a-python-class-not-unique-when-called-quickly) – user2357112 Feb 19 '16 at 21:16
  • 1
    You get the same ID when typing typing `id(a.f)` at the interactive prompt because the same memory address is being used to allocate the object each time. – kindall Feb 19 '16 at 21:16
  • 3
    @StevenRumbalski -- This has to do with the python memory allocator. As soon as you have printed the `id` of the function, no more references to that function exist and it gets reaped. It turns out that the memory allocator keeps pools of free memory and in this case, it creates the new bound method at the same memory location that the former one occupied (hence the same ID). Note that the ID is only guaranteed unique for the lifetime of an object. . . – mgilson Feb 19 '16 at 21:16
  • @mgilson: Aha! Thanks. – Steven Rumbalski Feb 19 '16 at 21:16
  • 1
    @StevenRumbalski -- I don't know too many of the allocator's details, but this turns out to be more common than you might think. Things like `id([]) == id([])` return `True` which obviously seems funky considering that `[] is []` is `False` :-) – mgilson Feb 19 '16 at 21:20