sintaxis patron decoradores clases python class methods decorator python-decorators

decoradores - patron decorator python



Decoradores y método de clase (2)

Dentro de una definición de clase, __call__ es una función, no un método. El acto de acceder a la función a través de la búsqueda de atributos (por ejemplo, usando la sintaxis de punto), como con Adder2.__call__ , devuelve un método Adder2.__call__ . Y a2.__call__ devuelve un método vinculado (con self bound a a2 ).

Tenga en cuenta que en Python3 el concepto de unbound method independiente ha sido eliminado. Allí, Adder2.__call__ es una función.

Tengo problemas para entender por qué sucede lo siguiente. Estoy teniendo un decorador que no hace nada, excepto que comprueba si una función es un método. Creí haber entendido qué método es Python, pero obviamente este no es el caso:

import inspect def deco(f): def g(*args): print inspect.ismethod(f) return f(*args) return g class Adder: @deco def __call__(self, a): return a + 1 class Adder2: def __call__(self, a): return a + 2 Adder2.__call__ = deco(Adder2.__call__)

Ahora, ejecuta lo siguiente:

>>> a = Adder() >>> a(1) False 2 >>> a2 = Adder2() >>> a2(1) True 3

Esperaría que este código imprimiera True dos veces.

Entonces, ¿decorar la función manualmente como en Adder2 no es un equivalente exacto a la decoración a través de la función @deco?

¿Puede alguien estar tan contento y explicar por qué sucede esto?


Los métodos son funciones que están asociadas con una clase. Los métodos solo se crean cuando los recuperas de una clase ya definida; un método es un envoltorio alrededor de una función, con una referencia también a la clase (y opcionalmente una referencia a la instancia).

Lo que sucede en el primer caso es: Python compila su definición de clase Adder . Encuentra la definición del decorador y una función. El decorador pasa la función y devuelve una nueva función. Esa función se agrega a la definición de clase (almacenada en la clase __dict__ ). Todo este tiempo se trata de una función python, no de un método. Eso pasa luego

Cuando luego llamas a(1) , una búsqueda revela que la instancia no tiene un __call__ pero la clase Adder sí lo hace, por lo que se recupera usando __getattribute__() . Esto encuentra una función (tu deco ), que es un descriptor por lo que se llama el método __get__() (así Adder.__call__.__get__(a, Adder)) , devolviendo un método bound , que luego se llama y pasa en el 1 valor. El método está vinculado porque instance no es Ninguno cuando se __get__() . Su decorador, que envolvió una función en el tiempo de construcción de la clase, imprime False porque se pasó una función desenvuelta para comenzar.

En el segundo caso, sin embargo, recuperas un método (de nuevo a través de __getattribute__() llamando a __get__() en la función Adder2.__call__ sin decorar), esta vez sin consolidar (ya que no hay instancia, solo se pasa una clase a __get__() (el la llamada completa es Adder2.__call__.__get__(None, Adder2) ), y luego decora ese método. Ahora ismethod() imprime True.

Tenga en cuenta que en Python 3, el último caso cambia. En Python 3 ya no existe un concepto de método independiente, solo funciones y métodos enlazados. El término ''límite'' se descarta por completo. Su segundo caso también imprimirá False como Adder2.__call__.__get__(None, Adder2) devuelve una función en ese caso.