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.