Do object methods of the same class have the same identifier?

class parent(object): @classmethod def a_class_method(cls): print "in class method %s" % cls @staticmethod def a_static_method(): print "static method" def useless_func(self): pass p1, p2 = parent(),parent() id(p1) == id(p2) // False id(p1.useless_func) == id(p2.useless_func) // True 

In the code above, I don't understand why useless_func has the same identifier when it belongs to two different objects?

+8
python
source share
2 answers

That's what I think:

  • When you search for p1.useless_func , a copy of it is created in memory. This memory is returned id
  • Since there are no links to a copy of the newly created method, it receives a correction from the GC, and the memory address is again available
  • When you p2.useless_func for p2.useless_func , a copy of it is created in the same memory address (it was available), which you retrieve using id again.
  • Second Copy - GCd

If you ran a bunch of different code and checked the identifiers of the instance methods again, I would promise that the id will be identical to each other, but different from the original run.

In addition, you may notice that in David Volver’s example, as soon as a long reference to a copy of the method is received, id will become different.

To confirm this theory, here is a shell session using Jython (same result with PyPy) that does not use reference counting on CPython counting:

 Jython 2.5.2 (Debian:hg/91332231a448, Jun 3 2012, 09:02:34) [OpenJDK Server VM (Oracle Corporation)] on java1.7.0_21 Type "help", "copyright", "credits" or "license" for more information. >>> class parent(object): ... def m(self): ... pass ... >>> p1, p2 = parent(), parent() >>> id(p1.m) == id(p2.m) False 
+7
source share

This is a very interesting question!

In your conditions, they look the same:

 Python 2.7.2 (default, Oct 11 2012, 20:14:37) [GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> class Foo(object): ... def method(self): pass ... >>> a, b = Foo(), Foo() >>> a.method == b.method False >>> id(a.method), id(b.method) (4547151904, 4547151904) 

However, note that as soon as you do something with them, they become different:

 >>> a_m = a.method >>> b_m = b.method >>> id(a_m), id(b_m) (4547151*9*04, 4547151*5*04) 

And then, having tested again, they changed again!

 >>> id(b.method) 4547304416 >>> id(a.method) 4547304416 

When accessing an instance method, an instance of the "associated method" is returned. The associated method stores a reference to both the instance and the object of the method function:

 >>> a_m <bound method Foo.method of <__main__.Foo object at 0x10f0e9a90>> >>> a_m.im_func is Foo.__dict__['method'] True >>> a_m.im_self is a True 

(note that I need to use Foo.__dict__['method'] , not Foo.method , because Foo.method will give a "unrelated method" ... whose purpose remains as an exercise for the reader)

The purpose of this “related method” object is to make the methods “behave rationally” when they are passed as similar functions. For example, when I call the function a_m() , which is identical to calling a.method() , although we no longer have an explicit reference to a . Compare this behavior with JavaScript (for example), where var method = foo.method; method() var method = foo.method; method() does not give the same result as foo.method() .

SO! This brings us back to the first question: why does id(a.method) give the same meaning as id(b.method) ? I believe that Asad is right: this is related to counting the Python * garbage collector: when the id(a.method) expression id(a.method) , the associated method is distributed, the identifier is calculated, and the binding method is freed. When the next associated method is allocated - for b.method -, it is allocated exactly in the same place in memory, since there were no (or there was a balanced number) allocations, since the associated method for a.method was allocated. This means that a.method seems to have the same memory location as b.method .

Finally, this explains why the memory cells seem to change the second time they are checked: other allocations that occurred between the first and second checks mean that the second time they are allocated elsewhere (note: they are redistributed, because all references to them were lost, related methods are cached †, therefore, access to the same method returns the same instance twice: a_m0 = a.method; a_m1 = a.method; a_m0 is a_m1 => True ).

*: note of pedants: in fact, this has nothing to do with the actual garbage collector, which exists only for working with circular links ... but ... this is a story the other day. †: at least in CPython 2.7; CPython 2.6 does not seem to cache related methods, which would lead me to expect that the behavior is not specified.

+11
source share

All Articles