example - ¿Qué hace el símbolo “at”(@) en Python?
python decorator with arguments (11)
¿Qué hace el símbolo “at” (@) en Python?
El símbolo @ es un azúcar sintáctico que Python proporciona para utilizar decorator
,
Parafraseando la pregunta, ¿se trata exactamente de qué hace el decorador en Python?
En otras palabras, el decorator
simple le permite modificar la definición de una función determinada sin tocar su interior (su cierre).
Es el caso más importante cuando importa un paquete maravilloso de un tercero. Puedes visualizarlo, puedes usarlo, pero no puedes tocar su interior y su corazón.
Aquí hay un ejemplo rápido,
Supongamos que defino una función read_a_book en Ipython
In [9]: def read_a_book():
...: return "I am reading the book: "
...:
In [10]: read_a_book()
Out[10]: ''I am reading the book: ''
Verás, me olvidé de añadirle un nombre.
¿Cómo resolver tal problema? Por supuesto, podría redefinir la función como:
def read_a_book():
return "I am reading the book: ''Python Cookbook''"
Sin embargo, ¿qué sucede si no se me permite manipular la función original o si hay miles de funciones que deben manejarse?
Resuelve el problema pensando diferente y define una nueva_función.
def add_a_book(func):
def wrapper():
return func() + "Python Cookbook"
return wrapper
Entonces emplearlo.
In [14]: read_a_book = add_a_book(read_a_book)
In [15]: read_a_book()
Out[15]: ''I am reading the book: Python Cookbook''
Tada, verás, read_a_book
sin tocar el cierre interno. Nada me detiene equipado con decorator
.
Que hay de @
@add_a_book
def read_a_book():
return "I am reading the book: "
In [17]: read_a_book()
Out[17]: ''I am reading the book: Python Cookbook''
@add_a_book
es una forma elegante y práctica de decir read_a_book = add_a_book(read_a_book)
, es un azúcar sintáctico, no hay nada más sofisticado al respecto.
Estoy viendo un código Python que usa el símbolo @
, pero no tengo idea de lo que hace. Tampoco sé qué buscar cuando busco documentos de Python o Google no devuelve resultados relevantes cuando se incluye el símbolo @
.
¿Qué hace el símbolo “at” (@) en Python?
En resumen, se utiliza en la sintaxis del decorador y para la multiplicación de matrices.
En el contexto de los decoradores, esta sintaxis:
@decorator
def decorated_function():
"""this function is decorated"""
es equivalente a esto:
def decorated_function():
"""this function is decorated"""
decorated_function = decorator(decorated_function)
En el contexto de la multiplicación de matrices, a @ b
invoca a.__matmul__(b)
- haciendo esta sintaxis:
a @ b
equivalente a
dot(a, b)
y
a @= b
equivalente a
a = dot(a, b)
donde dot
es, por ejemplo, la función de multiplicación de matrices numpy y a
y b
son matrices.
¿Cómo podrías descubrir esto por tu cuenta?
Tampoco sé qué buscar cuando busco documentos de Python o Google no devuelve resultados relevantes cuando se incluye el símbolo @.
Si desea tener una vista bastante completa de lo que hace una pieza de la sintaxis de Python, mire directamente el archivo de gramática. Para la rama de Python 3:
~$ grep -C 1 "@" cpython/Grammar/Grammar
decorator: ''@'' dotted_name [ ''('' [arglist] '')'' ] NEWLINE
decorators: decorator+
--
testlist_star_expr: (test|star_expr) ('','' (test|star_expr))* ['','']
augassign: (''+='' | ''-='' | ''*='' | ''@='' | ''/='' | ''%='' | ''&='' | ''|='' | ''^='' |
''<<='' | ''>>='' | ''**='' | ''//='')
--
arith_expr: term ((''+''|''-'') term)*
term: factor ((''*''|''@''|''/''|''%''|''//'') factor)*
factor: (''+''|''-''|''~'') factor | power
Podemos ver aquí que @
se usa en tres contextos:
- decoradores
- un operador entre factores
- un operador de asignación aumentada
Sintaxis del decorador:
Una búsqueda en Google de "decorator python docs" ofrece como uno de los mejores resultados, la sección "Declaraciones compuestas" de la "Referencia del lenguaje Python". Desplácese hasta la sección sobre definiciones de funciones , que podemos encontrar buscando la palabra "decorador", vemos que ... hay mucho para leer. Pero la palabra "decorador" es un enlace al glosario , que nos dice:
decorador
Una función que devuelve otra función, generalmente se aplica como una transformación de función utilizando la sintaxis de
@wrapper
. Ejemplos comunes para decoradores sonclassmethod()
ystaticmethod()
.La sintaxis del decorador es simplemente azúcar sintáctica, las siguientes dos definiciones de funciones son semánticamente equivalentes:
def f(...): ... f = staticmethod(f) @staticmethod def f(...): ...
El mismo concepto existe para las clases, pero se usa con menos frecuencia allí. Consulte la documentación para definiciones de funciones y definiciones de clases para obtener más información sobre decoradores.
Entonces, vemos que
@foo
def bar():
pass
es semánticamente lo mismo que:
def bar():
pass
bar = foo(bar)
No son exactamente iguales porque Python evalúa la expresión foo (que podría ser una búsqueda de puntos y una llamada de función) antes de la barra con la sintaxis del decorador ( @
), pero evalúa la expresión foo después de la barra en el otro caso.
(Si esta diferencia hace una diferencia en el significado de su código, debe reconsiderar lo que está haciendo con su vida, porque eso sería patológico).
Decoradores apilados
Si volvemos a la documentación de sintaxis de definición de función, vemos:
@f1(arg) @f2 def func(): pass
es aproximadamente equivalente a
def func(): pass func = f1(arg)(f2(func))
Esta es una demostración de que podemos llamar a una función que es un decorador primero, así como a los decoradores de pila. Las funciones, en Python, son objetos de primera clase, lo que significa que puede pasar una función como argumento a otra función y devolver funciones. Los decoradores hacen ambas cosas.
Si apilamos a los decoradores, la función, como se define, se pasa primero al decorador que está inmediatamente arriba, luego a la siguiente, y así sucesivamente.
Eso resume el uso de @
en el contexto de los decoradores.
El Operador, @
En la sección de análisis léxico de la referencia del lenguaje, tenemos una sección sobre operadores , que incluye @
, que también lo hace un operador:
Los siguientes tokens son operadores:
+ - * ** / // % @ << >> & | ^ ~ < > <= >= == !=
y en la siguiente página, el modelo de datos, tenemos la sección, emulando tipos numéricos ,
object.__add__(self, other) object.__sub__(self, other) object.__mul__(self, other) object.__matmul__(self, other) object.__truediv__(self, other) object.__floordiv__(self, other)
[...] Se llama a estos métodos para implementar las operaciones aritméticas binarias (
+
,-
,*
,@
,/
,//
, [...]
Y vemos que __matmul__
corresponde a @
. Si buscamos en la documentación "matmul" obtenemos un enlace a Novedades en Python 3.5 con "matmul" bajo el encabezado "PEP 465 - Un operador de infijo dedicado para la multiplicación de matrices".
se puede implementar definiendo
__matmul__()
,__rmatmul__()
y__imatmul__()
para la multiplicación de matrices regular, reflejada e in situ.
(Así que ahora aprendemos que @=
es la versión in situ). Además explica:
La multiplicación de matrices es una operación notablemente común en muchos campos de las matemáticas, la ciencia, la ingeniería, y la adición de @ permite escribir un código más limpio:
S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)
en lugar de:
S = dot((dot(H, beta) - r).T, dot(inv(dot(dot(H, V), H.T)), dot(H, beta) - r))
Si bien este operador se puede sobrecargar para hacer casi cualquier cosa, en numpy
, por ejemplo, numpy
esta sintaxis para calcular el producto interno y externo de matrices y matrices:
>>> from numpy import array, matrix
>>> array([[1,2,3]]).T @ array([[1,2,3]])
array([[1, 2, 3],
[2, 4, 6],
[3, 6, 9]])
>>> array([[1,2,3]]) @ array([[1,2,3]]).T
array([[14]])
>>> matrix([1,2,3]).T @ matrix([1,2,3])
matrix([[1, 2, 3],
[2, 4, 6],
[3, 6, 9]])
>>> matrix([1,2,3]) @ matrix([1,2,3]).T
matrix([[14]])
Multiplicación de matriz in situ: @=
Mientras investigamos el uso anterior, aprendemos que también existe la multiplicación de matrices in situ. Si intentamos usarlo, podemos encontrar que aún no está implementado para numpy:
>>> m = matrix([1,2,3])
>>> m @= m.T
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: In-place matrix multiplication is not (yet) supported. Use ''a = a @ b'' instead of ''a @= b''.
Cuando se implemente, esperaría que el resultado se vea así:
>>> m = matrix([1,2,3])
>>> m @= m.T
>>> m
matrix([[14]])
Preámbulo
Admito que me tomó más que unos pocos minutos comprender este concepto por completo, así que compartiré lo que aprendí para salvar a otros de los problemas.
El decorador de nombres, lo que definimos con la sintaxis @
antes de la definición de una función, fue probablemente el principal culpable aquí.
Ejemplo
class Pizza(object):
def __init__(self):
self.toppings = []
def __call__(self, topping):
# When using ''@instance_of_pizza'' before a function definition
# the function gets passed onto ''topping''.
self.toppings.append(topping())
def __repr__(self):
return str(self.toppings)
pizza = Pizza()
@pizza
def cheese():
return ''cheese''
@pizza
def sauce():
return ''sauce''
print pizza
# [''cheese'', ''sauce'']
Esto muestra que la function
/ method
/ class
que está definiendo después de un decorador simplemente se pasa como un argument
a la function
/ method
inmediatamente después del signo @
.
Primer avistamiento
El microframework Flask presenta a los decoradores desde el principio en el siguiente formato:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
Esto a su vez se traduce en:
rule = "/"
view_func = hello
# They go as arguments here in ''flask/app.py''
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
pass
Darme cuenta de esto finalmente me permitió sentirme en paz con Flask.
Comenzando con Python 3.5, el ''@'' se usa como un símbolo de infijo dedicado para MATRIX MULTIPLICATION (PEP 0465 - vea https://www.python.org/dev/peps/pep-0465/ )
El símbolo @ también se usa para acceder a las variables dentro de una consulta de marco de datos de plydata / pandas, pandas.DataFrame.query
. Ejemplo:
df = pandas.DataFrame({''foo'': [1,2,15,17]})
y = 10
df >> query(''foo > @y'') # plydata
df.query(''foo > @y'') # pandas
En Python 3.5 puede sobrecargar @
como operador. Se llama __matmul__
, porque está diseñado para hacer la multiplicación de matrices, pero puede ser lo que quieras. Ver PEP465 para más detalles.
Esta es una implementación simple de la multiplicación de matrices.
class Mat(list):
def __matmul__(self, B):
A = self
return Mat([[sum(A[i][k]*B[k][j] for k in range(len(B)))
for j in range(len(B[0])) ] for i in range(len(A))])
A = Mat([[1,3],[7,5]])
B = Mat([[6,8],[4,2]])
print(A @ B)
Este código produce:
[[18, 14], [62, 66]]
Este fragmento de código:
def decorator(func):
return func
@decorator
def some_func():
pass
Es equivalente a este código:
def decorator(func):
return func
def some_func():
pass
some_func = decorator(some_func)
En la definición de decorador, puede agregar algunas cosas modificadas que una función no devolvería normalmente.
Indica que estás utilizando un decorador. Aquí está el ejemplo de Bruce Eckel del 2008.
Para decir lo que otros tienen de una manera diferente: sí, es un decorador.
En Python, es como:
- Creando una función (sigue en la llamada @)
- Llamando a otra función para operar en su función creada. Esto devuelve una nueva función. La función que llamas es el argumento de la @.
- Reemplazando la función definida con la nueva función devuelta.
Esto se puede usar para todo tipo de cosas útiles, hecho posible porque las funciones son objetos y solo las instrucciones necesarias.
Se utiliza un símbolo @
al principio de una línea para decoradores de clases, funciones y métodos.
Leer más aquí:
Los decoradores de Python más comunes con los que te encontrarás son:
Si ve una @
en el medio de una línea, eso es una cosa diferente, la multiplicación de matrices. Desplácese hacia abajo para ver otras respuestas que abordan el uso de @
.
Si se está refiriendo a algún código en un cuaderno de Python que está utilizando la biblioteca Numpy , entonces el @ operator
significa Matrix Multiplication . Por ejemplo:
import numpy as np
def forward(xi, W1, b1, W2, b2):
z1 = W1 @ xi + b1
a1 = sigma(z1)
z2 = W2 @ a1 + b2
return z2, a1