2012-04-04 8 views
23

Ver la información del ejemplo sencillo:¿Cómo distingue Python la función de devolución de llamada que es miembro de una clase?

class A: 
    def __init__(self, flag): 
    self.flag = flag 

    def func(self): 
    print self.flag 

a = A(1) 
b = A(2) 
callback_a = a.func 
callback_b = b.func 

callback_a() 
callback_b() 

El resultado es:

1 
2 

Se ejecuta como se esperaba. Pero tengo una pregunta. En C, la función de devolución de llamada se pasa como un puntero. En Python, debe tener una forma similar de hacer esto, para que la persona que llama conozca la dirección de la función. Pero en mi ejemplo, no solo se pasa el puntero a la función, sino también el parámetro (auto), porque el mismo método de la misma clase imprime resultados diferentes. Entonces mis preguntas son:

  1. ¿Existe tal método en Python que solo tenga una copia en la memoria? Mi significado es que el código de cualquier método solo tiene una copia, y en mi ejemplo, el método no se clonará. Creo que debería tener solo una copia, pero aquí todavía hago esta pregunta para obtener más entradas.

  2. Recuerdo que todo en Python es un objeto. Entonces en mi ejemplo, ¿hay dos instancias de función con diferentes parámetros pero solo una copia de código?

+1

Creo que crea un cierre? –

+0

Deberías estar usando clases newstyle. Subclase todo desde 'objeto 'o algo que lo subclasifique. – aaronasterling

+1

@aaronasterling: ¿Aunque no estoy seguro de que tenga demasiado impacto en la pregunta en cuestión? – jdi

Respuesta

15

Específico para CPython, solo hay una copia del objeto de función. Durante la creación de la instancia, la clase ajusta las funciones independientes en su espacio de nombres como métodos vinculados. Pero todos envuelven la misma función.

Aquí está su ejemplo expandido para mostrar lo que está sucediendo.

class A(object): 
    def __init__(self, flag): 
    self.flag = flag 

    def func(self): 
    print self.flag 

a = A(1) 
b = A(2) 

callback_a = a.func 
callback_b = b.func 

print "typeof(callback_a) = {0}".format(type(callback_a)) 
print "typeof(callback_b) = {0}".format(type(callback_b)) 

print "typeof(callback_a.__func__) = {0}".format(type(callback_a.__func__)) 
print "typeof(callback_b.__func__) = {0}".format(type(callback_b.__func__)) 

print "'callback_a.__func__ is callback_b.__func__' is {0}".format(callback_a.__func__ is callback_b.__func__) 

callback_a() 
callback_b() 

Este código salidas

typeof(callback_a) = <type 'instancemethod'> 
typeof(callback_b) = <type 'instancemethod'> 
typeof(callback_a.__func__) = <type 'function'> 
typeof(callback_b.__func__) = <type 'function'> 
'callback_a.__func__ is callback_b.__func__' is True 

Usted puede ver claramente, usando el operador is, que ambos instancemethod clases comparten el mismo objeto función.

+2

Me gustaría saber por qué esto se revocó. Si hay algo objetivamente mal, entonces me gustaría saberlo. – aaronasterling

+0

Lo que me parece realmente interesante es que esta 'impresión' 'callback_a es callback_b' es {0} ". Format (callback_a es callback_b)' prints * false *. Viniendo de Javascript y Perl Encuentro este comportamiento realmente sorprendente. ¿Hay documentos de Python que dicen más al respecto? –

+2

'callback_a es callback_b' es' False' porque cada uno es un 'instancemethod' que envuelve tanto la función ** como ** la instancia con la que se llama a ese método. Ambos comparten el mismo '__func__' (como se muestra en la publicación inicial), pero cada uno tiene una instancia de objeto' self' diferente con la que se llama a esa función. ¿Tener sentido? – PfhorSlayer

17

En Python, la devolución de llamada no es simplemente una referencia a una función de miembro. En cambio, está "vinculado" al objeto al que hace referencia cuando se creó. Por lo tanto, a.func crea un llamable que está vinculado a a, y b.func crea un llamable que está vinculado a b.

Python solo necesita una implementación de func() en la memoria, pero probablemente creará una o más funciones de "trampolín" en tiempo de ejecución para lograr la unión (no estoy seguro de los detalles internos sobre esto, y diferiría entre Implementaciones de Python de todos modos).

Si imprime id(callback_a) y id(callback_b) obtendrá diferentes resultados, mostrando que son realmente diferentes objetos invocables.

+1

Y si hace 'id (b.func) == id (a.func)' encontrará que apuntan a la misma memoria – jdi

+0

@jdi: ¿Puede decirme por qué 'id (a.func)' da un resultado diferente ¿cada vez? – mshsayem

+0

@mshsayem 'id' se implementa como la dirección de memoria del objeto en CPython. Por lo tanto, cada vez que se ejecuta el programa obtiene una ubicación diferente en la memoria. – aaronasterling

Cuestiones relacionadas