¿Por qué es esto falso? `SomeClass.method es SomeClass.method`

Toma este código por ejemplo:

class SomeClass(): def a_method(self): pass print(SomeClass.a_method is SomeClass.a_method) # Example 1: False print(SomeClass.a_method == SomeClass.a_method) # Example 2: True print(SomeClass().a_method is SomeClass().a_method) # Example 3: False print(SomeClass().a_method == SomeClass().a_method) # Example 4: False 
  • Ejemplo 1: habría adivinado que eran el mismo objeto. ¿Python hace una copia del método cada vez que se hace referencia?
  • Ejemplo 2: esperado.
  • Ejemplo 3: Esperado, ya que son objetos diferentes.
  • Ejemplo 4: ¿Por qué esta salida no coincide con el ejemplo 2?

Ejemplo 1:

Someclass.a_method es un método Someclass.a_method . Hoy en día, ni siquiera existen en Python, así que considera esta una lección de historia inútil.

¿Python hace una copia del método cada vez que se hace referencia?

Si, más o menos. Esto se hace a través del protocolo descriptor .

 >>> SomeClass.a_method # unbound method via attribute access  >>> SomeClass.__dict__['a_method'] # just stored as a function in the class dict  >>> SomeClass.__dict__['a_method'].__get__(None, SomeClass)  

La última línea muestra la operación de “enlace” que los descriptores invocan para el acceso a atributos en una clase, pero que se escriben manualmente. En puro Python, es algo como esto

 class Function(object): def __get__(self, obj, objtype=None): "Simulate func_descr_get() in Objects/funcobject.c" return types.MethodType(self, obj, objtype): 

También puede crear un método enlazado de esa manera:

 >>> some_instance = SomeClass() >>> SomeClass.__dict__['a_method'].__get__(some_instance, SomeClass) > 

Ejemplo 2:

La comparación de métodos se realiza a través de los __func__ y __self__ en los métodos. En este caso, ambos son idénticos: el __func__ es la misma función simple que puede extraer del __self__ de la clase, y el __self__ es None . Entonces a pesar de que estos métodos son objetos diferentes, se comparan iguales.

Ejemplo 3:

Correcto. Son objetos diferentes, y por lo tanto no son idénticos.

Ejemplo 4:

Como se mencionó anteriormente, la comparación está utilizando los atributos __func__ y __self__ . El resultado no coincide con el ejemplo 2 porque, en este caso, los atributos __self__ se refieren a diferentes instancias. Esas instancias diferentes no se comparan igual porque SomeClass instancias de SomeClass comparan por identidad, por lo tanto, los métodos tampoco se comparan igual.

Una nota final sobre la versión actual de Python

Todo lo mencionado anteriormente también se aplica a la versión actual del idioma, excepto en el Ejemplo 1 . En Python, ya no existe un método independiente, se eliminó esta complicación innecesaria en el modelo de objetos.

 >>> SomeClass.a_method  >>> SomeClass.a_method is SomeClass.__dict__['a_method'] True 

Lo que era un “método independiente” en Python 2 ahora es solo una función antigua y la instancia recuperada a través del acceso de atributo es idéntica al objeto en la clase dict. El resultado del Ejemplo 1 cambia de False a True en una actualización de Python 2 -> Python 3.