id () s de objetos de método enlazados y no enlazados – a veces lo mismo para diferentes objetos, a veces diferente para el mismo objeto

He intentado algún código sobre métodos enlazados y no unidos. Cuando los llamamos, creo que ambos devolverían objetos. Pero cuando uso id() para obtener información, devuelve algo que no entiendo.

IDE: Eclipse

Plugin: Pydev

 Class C(object): def foo(self): pass cobj = C() print id(C.foo) #1 print id(cobj.foo) #2 a = C.foo b = cobj.foo print id(a) #3 print id(b) #4 

Y la salida es …

 5671672 5671672 5671672 5669368 

¿Por qué los números 1 y 2 devuelven la misma ID? ¿No son objetos diferentes? Y si asignamos C.foo y conj.foo a dos variables, # 3 y # 4 devuelven la identificación diferente.

Creo que # 3 y # 4 muestran que no son el mismo objeto, pero # 1 y # 2 …

¿Cuál es la diferencia entre la identificación del método enlazado y un método independiente?

Cada vez que busque un método a través de instance.name (y en Python 2, class.name ), el objeto del método se creará como nuevo. Python usa el protocolo descriptor para envolver la función en un objeto de método cada vez.

Por lo tanto, cuando busca id(C.foo) , se crea un nuevo objeto de método, recupera su id (una dirección de memoria) y luego vuelve a descartar el objeto de método . Luego busca id(cobj.foo) , un nuevo objeto de método creado que reutiliza la dirección de memoria ahora liberada y ve el mismo valor. Luego, el método se descarta nuevamente (la basura se recolecta a medida que el recuento de referencia se reduce a 0).

A continuación, almacenó una referencia al método C.foo en una variable. Ahora la dirección de la memoria no se libera (el recuento de referencia es 1, en lugar de 0), y crea una segunda instancia de método buscando en cobj.foo que tiene que usar una nueva ubicación de memoria. Así obtienes dos valores diferentes.

Consulte la documentación para id() :

Devuelve la “identidad” de un objeto. Este es un entero (o entero largo) que se garantiza que será único y constante para este objeto durante su vida útil. Dos objetos con tiempos de vida no superpuestos pueden tener el mismo valor id() .

Detalle de implementación de CPython : Esta es la dirección del objeto en la memoria.

Énfasis mío.

Puede volver a crear un método utilizando una referencia directa a la función a través del atributo __dict__ de la clase, y luego llamar al método descriptor __get__ :

 >>> class C(object): ... def foo(self): ... pass ... >>> C.foo  >>> C.__dict__['foo']  >>> C.__dict__['foo'].__get__(None, C)  >>> C.__dict__['foo'].__get__(C(), C) > 

Tenga en cuenta que en Python 3, se ha descartado toda la distinción entre métodos no vinculados / enlazados; obtienes una función donde antes obtendrías un método no vinculado, y otro método, donde un método siempre está vinculado:

 >>> C.foo  >>> C.foo.__get__(None, C)  >>> C.foo.__get__(C(), C) > 

Además, Python 3.7 agrega un nuevo par de opcode LOAD_METHODCALL_METHOD que reemplaza al par de opcode actual LOAD_ATTRIBUTECALL_FUNCTION precisamente para evitar crear un nuevo objeto de método cada vez. Esta optimización transforma la ruta de ejecución para instance.foo() del type(instance).__dict__['foo'].__get__(instance, type(instance))() con el type(instance).__dict__['foo'](instance) , por lo que ‘manualmente’ pasa la instancia directamente al objeto de función.

Añadiendo a la muy buena respuesta de @Martijn Pieters:

 In [1]: class C(object): ...: def foo(self): ...: pass ...: In [2]: c = C() In [3]: id(c.foo), id(C.foo) Out[3]: (149751844, 149751844) # so 149751844 is current free memory address In [4]: a = c.foo # now 149751844 is assigned to a In [5]: id(a) Out[5]: 149751844 # now python will allocate some different address to c.foo and C.foo In [6]: id(c.foo), id(C.foo) # different address used this time, and Out[6]: (149752284, 149752284) # that address is freed after this step # now 149752284 is again free, as it was not allocated to any variable In [7]: b = C.foo # now 149752284 is allocated to b In [8]: id(b) Out[8]: 149752284 In [9]: c.foo is C.foo # better use `is` to compare objects, rather than id() Out[9]: False