Risposte:
Un @
simbolo all'inizio di una riga viene utilizzato per decoratori di classi, funzioni e metodi .
Leggi di più qui:
I più comuni decoratori di Python in cui ti imbatterai sono:
Se vedi un @
al centro di una linea, è una cosa diversa, la moltiplicazione della matrice. Scorri verso il basso per vedere altre risposte che affrontano quell'uso di @
.
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']
Ciò mostra che il function
/ method
/ class
che stai definendo dopo che un decoratore è sostanzialmente passato argument
al function
/ method
immediatamente dopo il @
segno.
Il pallone microframe introduce i decoratori fin dall'inizio nel seguente formato:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
Questo a sua volta si traduce in:
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
Capire questo alla fine mi ha permesso di sentirmi in pace con Flask.
app.route("/")
: questa funzione restituisce una funzione, che invochi con il tuo hello()
come argomento
app.route("/", hello)
immediatamente dopo aver definito hello
, o persino definire hello
come lambda negli argomenti a app.route
? (Quest'ultimo esempio è comune con le http.Server
rotte Node.js ed Express.)
Questo frammento di codice:
def decorator(func):
return func
@decorator
def some_func():
pass
È equivalente a questo codice:
def decorator(func):
return func
def some_func():
pass
some_func = decorator(some_func)
Nella definizione di decoratore puoi aggiungere alcune cose modificate che non verrebbero normalmente restituite da una funzione.
In Python 3.5 puoi sovraccaricare @
come operatore. È chiamato come __matmul__
, perché è progettato per eseguire la moltiplicazione di matrici, ma può essere qualsiasi cosa tu voglia. Vedi PEP465 per i dettagli.
Questa è una semplice implementazione della moltiplicazione di matrici.
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)
Questo codice produce:
[[18, 14], [62, 66]]
@=
operatore (sul posto), che è __imatmul__
.
__add__
e __sub__
sono collegati rispettivamente a + e -, ma non ho mai sentito parlare del @
segno prima. Ce ne sono altri in agguato là fuori?
In breve, viene utilizzato nella sintassi del decoratore e per la moltiplicazione della matrice.
Nel contesto dei decoratori, questa sintassi:
@decorator
def decorated_function():
"""this function is decorated"""
è equivalente a questo:
def decorated_function():
"""this function is decorated"""
decorated_function = decorator(decorated_function)
Nel contesto della moltiplicazione di matrici, a @ b
invoca a.__matmul__(b)
- creando questa sintassi:
a @ b
equivalente a
dot(a, b)
e
a @= b
equivalente a
a = dot(a, b)
dove si dot
trova, ad esempio, la funzione di moltiplicazione della matrice numpy e a
e b
sono matrici.
Inoltre non so cosa cercare poiché la ricerca di documenti Python o Google non restituisce risultati pertinenti quando è incluso il simbolo @.
Se vuoi avere una visione piuttosto completa di ciò che fa una particolare sintassi di Python, guarda direttamente il file di grammatica. Per il ramo 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
Possiamo vedere qui che @
viene utilizzato in tre contesti:
Una ricerca su Google per "decorator python docs" fornisce come uno dei migliori risultati la sezione "Dichiarazioni composte" della "Guida di riferimento di Python". Scorrendo verso il basso fino alla sezione sulle definizioni delle funzioni , che possiamo trovare cercando la parola "decoratore", vediamo che ... c'è molto da leggere. Ma la parola "decoratore" è un collegamento al glossario , che ci dice:
decoratore
Una funzione che restituisce un'altra funzione, generalmente applicata come trasformazione di una funzione usando la
@wrapper
sintassi. Esempi comuni per decoratori sonoclassmethod()
estaticmethod()
.La sintassi del decoratore è semplicemente zucchero sintattico, le seguenti due definizioni di funzione sono semanticamente equivalenti:
def f(...): ... f = staticmethod(f) @staticmethod def f(...): ...
Lo stesso concetto esiste per le classi, ma è meno comunemente usato lì. Consulta la documentazione per le definizioni delle funzioni e le definizioni delle classi per ulteriori informazioni sui decoratori.
Quindi, lo vediamo
@foo
def bar():
pass
è semanticamente uguale a:
def bar():
pass
bar = foo(bar)
Non sono esattamente gli stessi perché Python valuta l'espressione foo (che potrebbe essere una ricerca tratteggiata e una chiamata di funzione) prima della barra con la @
sintassi di decorator ( ), ma valuta l'espressione foo dopo la barra nell'altro caso.
(Se questa differenza fa la differenza nel significato del tuo codice, dovresti riconsiderare cosa stai facendo della tua vita, perché sarebbe patologico.)
Se torniamo alla documentazione sulla sintassi della definizione della funzione, vediamo:
@f1(arg) @f2 def func(): pass
è approssimativamente equivalente a
def func(): pass func = f1(arg)(f2(func))
Questa è una dimostrazione che possiamo chiamare una funzione che è prima un decoratore, così come i decoratori dello stack. Le funzioni, in Python, sono oggetti di prima classe, il che significa che puoi passare una funzione come argomento a un'altra funzione e restituire funzioni. I decoratori fanno entrambe queste cose.
Se impiliamo i decoratori, la funzione, come definita, viene passata prima al decoratore immediatamente sopra di esso, quindi al successivo e così via.
Quello su riassume l'uso per @
nel contesto dei decoratori.
@
Nella sezione di analisi lessicale del riferimento linguistico, abbiamo una sezione sugli operatori , che include @
, che lo rende anche un operatore:
I seguenti token sono operatori:
+ - * ** / // % @ << >> & | ^ ~ < > <= >= == !=
e nella pagina successiva, il modello di dati, abbiamo la sezione Emulazione di tipi numerici ,
object.__add__(self, other) object.__sub__(self, other) object.__mul__(self, other) object.__matmul__(self, other) object.__truediv__(self, other) object.__floordiv__(self, other)
[...] Questi metodi sono chiamati ad attuare le operazioni aritmetiche binarie (
+
,-
,*
,@
,/
,//
, [...]
E vediamo che __matmul__
corrisponde a @
. Se cerchiamo la documentazione per "matmul" otteniamo un collegamento a Novità di Python 3.5 con "matmul" sotto la voce "PEP 465 - Un operatore infix dedicato per la moltiplicazione di matrici".
può essere implementato definendo
__matmul__()
,__rmatmul__()
e__imatmul__()
per la moltiplicazione della matrice regolare, riflessa e sul posto.
(Quindi ora apprendiamo che @=
è la versione sul posto). Spiega inoltre:
La moltiplicazione delle matrici è un'operazione particolarmente comune in molti campi della matematica, della scienza, dell'ingegneria e l'aggiunta di @ consente di scrivere codice più pulito:
S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)
invece di:
S = dot((dot(H, beta) - r).T, dot(inv(dot(dot(H, V), H.T)), dot(H, beta) - r))
Mentre questo operatore può essere sovraccaricato per fare praticamente qualsiasi cosa, numpy
ad esempio, utilizzeremmo questa sintassi per calcolare il prodotto interno ed esterno di matrici e matrici:
>>> 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]])
@=
Durante la ricerca dell'uso precedente, apprendiamo che esiste anche la moltiplicazione della matrice sul posto. Se tentiamo di usarlo, potremmo scoprire che non è ancora implementato per 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'.
Quando viene implementato, mi aspetto che il risultato sia simile al seguente:
>>> m = matrix([1,2,3])
>>> m @= m.T
>>> m
matrix([[14]])
Cosa fa il simbolo "at" (@) in Python?
Il simbolo @ è un pitone dello zucchero sintattico da utilizzare decorator
,
per parafrasare la domanda, riguarda esattamente cosa fa il decoratore in Python?
In parole semplici, decorator
consente di modificare la definizione di una determinata funzione senza toccarne l'interno (è la chiusura).
È il caso più importante quando si importano meravigliosi pacchetti da terze parti. Puoi visualizzarlo, puoi usarlo, ma non puoi toccarne il più profondo e il suo cuore.
Ecco un breve esempio,
supponiamo che io definisca una read_a_book
funzione su 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: '
Vedi, ho dimenticato di aggiungere un nome.
Come risolvere un simile problema? Certo, potrei ridefinire la funzione come:
def read_a_book():
return "I am reading the book: 'Python Cookbook'"
Tuttavia, cosa succede se non mi è permesso manipolare la funzione originale o se ci sono migliaia di tale funzione da gestire.
Risolvi il problema pensando diversamente e definendo una nuova funzione
def add_a_book(func):
def wrapper():
return func() + "Python Cookbook"
return wrapper
Quindi impiegalo.
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, vedi, ho modificato read_a_book
senza toccarlo chiusura interna. Niente mi impedisce di essere equipaggiato decorator
.
Di cosa si tratta @
@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
è un modo elegante e pratico per dire read_a_book = add_a_book(read_a_book)
, è uno zucchero sintattico, non c'è niente di più fantasioso al riguardo.
Se ti riferisci ad un codice in un notebook Python che utilizza la libreria Numpy , allora @ operator
significa Moltiplicazione di matrici . Per esempio:
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
A partire da Python 3.5, "@" viene utilizzato come simbolo di infissione dedicato per MATRIX MULTIPLICATION (PEP 0465 - vedi https://www.python.org/dev/peps/pep-0465/ )
I decoratori sono stati aggiunti in Python per semplificare la lettura e la comprensione del wrapping di funzioni e metodi (una funzione che riceve una funzione e ne restituisce una migliorata). Il caso d'uso originale doveva essere in grado di definire i metodi come metodi di classe o metodi statici sulla testa della loro definizione. Senza la sintassi del decoratore, richiederebbe una definizione piuttosto sparsa e ripetitiva:
class WithoutDecorators:
def some_static_method():
print("this is static method")
some_static_method = staticmethod(some_static_method)
def some_class_method(cls):
print("this is class method")
some_class_method = classmethod(some_class_method)
Se la sintassi del decoratore viene utilizzata per lo stesso scopo, il codice è più breve e più facile da capire:
class WithDecorators:
@staticmethod
def some_static_method():
print("this is static method")
@classmethod
def some_class_method(cls):
print("this is class method")
Sintassi generale e possibili implementazioni
Il decoratore è generalmente un oggetto con nome ( non sono ammesse espressioni lambda ) che accetta un singolo argomento quando viene chiamato (sarà la funzione decorata) e restituisce un altro oggetto richiamabile. "Callable" è usato qui invece di "funzione" con premeditazione. Mentre i decoratori sono spesso discussi nell'ambito dei metodi e delle funzioni, non si limitano a loro. In effetti, tutto ciò che è richiamabile (qualsiasi oggetto che implementa il metodo _call__ è considerato richiamabile), può essere usato come decoratore e spesso gli oggetti restituiti da loro non sono semplici funzioni ma più istanze di classi più complesse che implementano il proprio metodo __call_.
La sintassi del decoratore è semplicemente solo uno zucchero sintattico . Considera il seguente utilizzo del decoratore:
@some_decorator
def decorated_function():
pass
Questo può sempre essere sostituito da una chiamata esplicativa del decoratore e dalla riassegnazione della funzione:
def decorated_function():
pass
decorated_function = some_decorator(decorated_function)
Tuttavia, quest'ultimo è meno leggibile e anche molto difficile da capire se su una singola funzione vengono utilizzati più decoratori. I decoratori possono essere utilizzati in diversi modi come mostrato di seguito:
Come una funzione
Esistono molti modi per scrivere decoratori personalizzati, ma il modo più semplice è scrivere una funzione che restituisce una sottofunzione che avvolge la chiamata di funzione originale.
I modelli generici sono i seguenti:
def mydecorator(function):
def wrapped(*args, **kwargs):
# do some stuff before the original
# function gets called
result = function(*args, **kwargs)
# do some stuff after function call and
# return the result
return result
# return wrapper as a decorated function
return wrapped
Come una classe
Mentre i decoratori possono quasi sempre essere implementati usando le funzioni, ci sono alcune situazioni in cui l'uso delle classi definite dall'utente è un'opzione migliore. Questo è spesso vero quando il decoratore ha bisogno di una parametrizzazione complessa o dipende da uno stato specifico.
Il modello generico per un decoratore non parametrizzato come classe è il seguente:
class DecoratorAsClass:
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
# do some stuff before the original
# function gets called
result = self.function(*args, **kwargs)
# do some stuff after function call and
# return the result
return result
Parametrizzare i decoratori
Nel codice reale, è spesso necessario utilizzare decoratori che possono essere parametrizzati. Quando la funzione viene utilizzata come decoratore, la soluzione è semplice: è necessario utilizzare un secondo livello di avvolgimento. Ecco un semplice esempio del decoratore che ripete l'esecuzione di una funzione decorata il numero specificato di volte ogni volta che viene chiamato:
def repeat(number=3):
"""Cause decorated function to be repeated a number of times.
Last value of original function call is returned as a result
:param number: number of repetitions, 3 if not specified
"""
def actual_decorator(function):
def wrapper(*args, **kwargs):
result = None
for _ in range(number):
result = function(*args, **kwargs)
return result
return wrapper
return actual_decorator
Il decoratore definito in questo modo può accettare parametri:
>>> @repeat(2)
... def foo():
... print("foo")
...
>>> foo()
foo
foo
Si noti che anche se il decoratore parametrizzato ha valori predefiniti per i suoi argomenti, sono necessarie le parentesi dopo il suo nome. Il modo corretto di utilizzare il decoratore precedente con argomenti predefiniti è il seguente:
>>> @repeat()
... def bar():
... print("bar")
...
>>> bar()
bar
bar
bar
Finalmente vediamo i decoratori con Proprietà.
Proprietà
Le proprietà forniscono un tipo di descrittore incorporato che sa come collegare un attributo a una serie di metodi. Una proprietà accetta quattro argomenti opzionali: fget, fset, fdel e doc. L'ultimo può essere fornito per definire una docstring collegata all'attributo come se fosse un metodo. Ecco un esempio di una classe Rectangle che può essere controllata mediante l'accesso diretto agli attributi che memorizzano due punti d'angolo o usando le proprietà width e height:
class Rectangle:
def __init__(self, x1, y1, x2, y2):
self.x1, self.y1 = x1, y1
self.x2, self.y2 = x2, y2
def _width_get(self):
return self.x2 - self.x1
def _width_set(self, value):
self.x2 = self.x1 + value
def _height_get(self):
return self.y2 - self.y1
def _height_set(self, value):
self.y2 = self.y1 + value
width = property(
_width_get, _width_set,
doc="rectangle width measured from left"
)
height = property(
_height_get, _height_set,
doc="rectangle height measured from top"
)
def __repr__(self):
return "{}({}, {}, {}, {})".format(
self.__class__.__name__,
self.x1, self.y1, self.x2, self.y2
)
La migliore sintassi per la creazione di proprietà è l'utilizzo della proprietà come decoratore. Ciò ridurrà il numero di firme dei metodi all'interno della classe e renderà il codice più leggibile e gestibile . Con i decoratori la classe sopra diventa:
class Rectangle:
def __init__(self, x1, y1, x2, y2):
self.x1, self.y1 = x1, y1
self.x2, self.y2 = x2, y2
@property
def width(self):
"""rectangle height measured from top"""
return self.x2 - self.x1
@width.setter
def width(self, value):
self.x2 = self.x1 + value
@property
def height(self):
"""rectangle height measured from top"""
return self.y2 - self.y1
@height.setter
def height(self, value):
self.y2 = self.y1 + value
Per dire ciò che gli altri hanno in un modo diverso: sì, è un decoratore.
In Python, è come:
Questo può essere usato per tutti i tipi di cose utili, reso possibile perché le funzioni sono oggetti e solo le necessarie istruzioni.
Indica che stai utilizzando un decoratore. Ecco l'esempio di Bruce Eckel del 2008.