¿Qué hace el símbolo “at” (@) en Python?

Estoy viendo un código Python que usa el símbolo @ , pero no tengo idea de lo que hace. Tampoco sé qué buscar cuando se buscan documentos de Python o Google no devuelve resultados relevantes cuando se incluye el símbolo @ .

Se utiliza un símbolo @ al principio de una línea para los decoradores de clases, funciones y métodos.

Leer más aquí:

PEP 318: Decoradores

Decoradores de python

Los decoradores de Python más comunes con los que te encontrarás son:

@propiedad

@classmethod

método estático

Si ves una @ en el medio de una línea, eso es otra cosa, la multiplicación de matrices. Desplácese hacia abajo para ver otras respuestas que abordan el uso de @ .

Preámbulo

Admito que me tomó más de 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 syntax @ 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.

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.

En Python 3.5 puede sobrecargar @ como operador. Se denomina como __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]] 

¿Qué hace el símbolo “at” (@) en Python?

En resumen, se usa en la syntax del decorador y para la multiplicación de matrices.

En el contexto de los decoradores, esta syntax:

 @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 syntax:

 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 se buscan 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 particular de la syntax de Python, mire directamente el archivo de gramática. Para la twig 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 de Python”. Desplácese hasta la sección de 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 syntax de @wrapper . Ejemplos comunes para decoradores son classmethod() y staticmethod() .

La syntax 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 syntax 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 astackdos

Si volvemos a la documentación de syntax 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 stack. 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 astackmos a los decoradores, la función, como se define, se pasa primero al decorador que está inmediatamente arriba, luego al 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 matemáticas, ciencias, ingeniería, y la adición de @ permite escribir código más limpio:

 S = (H @ beta - r).T @ inv(H @ V @ HT) @ (H @ beta - r) 

en lugar de:

 S = dot((dot(H, beta) - r).T, dot(inv(dot(dot(H, V), HT)), dot(H, beta) - r)) 

Si bien este operador se puede sobrecargar para hacer casi cualquier cosa, en numpy , por ejemplo, numpy esta syntax 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 @= mT Traceback (most recent call last): File "", line 1, in  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 @= mT >>> m matrix([[14]]) 

¿Qué hace el símbolo “at” (@) en Python?

El símbolo @ es un azúcar sintáctico que Python proporciona para utilizar el 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.

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 

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 dataframe 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 

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:

  1. Creando una función (sigue en la llamada @)
  2. 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 @.
  3. 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.