I believe that func(obj.attr) and tmp = obj.attr; func(tmp) should be completely indistinguishable. However, the following code
class Cls(object):
#@staticmethod
def f(self):
pass
def who(obj):
print(id(obj) % 1000, end=' ')
obj = Cls()
n = 10
for i in range(n):
who(obj.f)
print('\n')
for i in range(n):
tmp = obj.f
who(tmp)
produces
736 736 736 736 736 736 736 736 736 736
736 216 736 216 736 216 736 216 736 216
We can see that the second for loop produces a interesting pattern, while the first one does not. It seems that assigning a bound method would alter the bound method itself, but why passing it to a function wouldn't?
Python creates a new bound method object every time you access
f(see Does Python really create all bound method for every new instance?).In the first loop:
obj.fcreates the bound method object.whoprints the idobj.fcreates a new bound method object. The intrepreter reuses the memory freed at the iteration before.whoprints the id which happen to match the previous idIn the second loop:
obj.fcreates a new bound method objecttmp(and thus it has a reference now)whoprints the idtmpvariable keeps the object aliveobj.fcreates a new bound method object. It cannot reuse the old address because that object is still alive.tmp. Now the old value oftmphas no more references and thus is deallocated.whoprints the id, which is a new one.obj.fcreates a new bound method object. Now the old-old address is again available and the interpreter decides to reuse it.tmp. Now the old value oftmphas no more references and thus is deallocated.whoprints the id, which is a old-old oneand so a loop is created that alternates between the two addresses.
By using more variables you can create longer cycles:
Or, as mentioned by Reblochon Masque in his answer, using
delyou can avoid the behaviour:Note that this is due to implementation details of CPython. In Jython or other python implementations without reference counting both loops will probably behave pretty much in the same way: showing ten different ids.