Antworten:
Ein @
Symbol am Anfang einer Zeile ist für die Klasse, die Funktion und Verfahren verwendet Dekoratore .
Lesen Sie hier mehr:
Die häufigsten Python-Dekoratoren, denen Sie begegnen werden, sind:
Wenn Sie eine @
in der Mitte einer Linie sehen, ist das eine andere Sache, die Matrixmultiplikation. Scrollen Sie nach unten, um andere Antworten zu sehen, die sich mit dieser Verwendung befassen @
.
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']
Dies zeigt, dass das function
/ method
/, das class
Sie nach einem Dekorateur definieren, im Grunde genommen als / argument
an das function
/ method
unmittelbar nach dem @
Zeichen weitergegeben wird.
Die Mikroframework- Flasche stellt Dekorateure von Anfang an in folgendem Format vor:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
Dies bedeutet wiederum:
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
Als ich das erkannte, konnte ich mich endlich in Frieden mit Flask fühlen.
app.route("/")
: Diese Funktion gibt eine Funktion zurück, die Sie mit Ihrem hello()
als Argument
app.route("/", hello)
unmittelbar nach dem Definieren aufzurufen hello
oder sogar hello
als Lambda in den Argumenten zu definieren app.route
? (Das letztere Beispiel ist bei Node.js- http.Server
und Express-Routen üblich.)
Dieser Code-Ausschnitt:
def decorator(func):
return func
@decorator
def some_func():
pass
Entspricht diesem Code:
def decorator(func):
return func
def some_func():
pass
some_func = decorator(some_func)
In der Definition eines Dekorateurs können Sie einige modifizierte Dinge hinzufügen, die von einer Funktion normalerweise nicht zurückgegeben werden.
In Python 3.5 können Sie @
als Operator überladen . Es heißt __matmul__
, weil es für die Matrixmultiplikation ausgelegt ist, aber es kann alles sein, was Sie wollen. Siehe PEP465 für Details.
Dies ist eine einfache Implementierung der Matrixmultiplikation.
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)
Dieser Code ergibt:
[[18, 14], [62, 66]]
@=
Operator (an Ort und Stelle) __imatmul__
.
__add__
und bin __sub__
mit + bzw. - verbunden, habe aber noch nie von dem @
Zeichen 1 gehört. Lauern da draußen noch andere?
Kurz gesagt, es wird in der Decorator-Syntax und zur Matrixmultiplikation verwendet.
Im Kontext von Dekorateuren lautet diese Syntax:
@decorator
def decorated_function():
"""this function is decorated"""
ist gleichbedeutend damit:
def decorated_function():
"""this function is decorated"""
decorated_function = decorator(decorated_function)
Im Zusammenhang mit der Matrixmultiplikation, a @ b
aufruft a.__matmul__(b)
- so dass diese Syntax:
a @ b
gleichwertig
dot(a, b)
und
a @= b
gleichwertig
a = dot(a, b)
wo dot
ist zum Beispiel die Numpy-Matrix-Multiplikationsfunktion und a
und b
sind Matrizen.
Ich weiß auch nicht, wonach ich suchen soll, da die Suche in Python-Dokumenten oder Google keine relevanten Ergebnisse zurückgibt, wenn das @ -Symbol enthalten ist.
Wenn Sie einen ziemlich vollständigen Überblick über die Funktionsweise einer bestimmten Python-Syntax erhalten möchten, sehen Sie sich direkt die Grammatikdatei an. Für den Python 3-Zweig:
~$ 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
Wir können hier sehen, dass @
in drei Zusammenhängen verwendet wird:
Eine Google-Suche nach "Decorator Python Docs" liefert als eines der Top-Ergebnisse den Abschnitt "Compound Statements" der "Python Language Reference". Wenn wir zu dem Abschnitt über Funktionsdefinitionen scrollen , den wir finden können, indem wir nach dem Wort "Dekorateur" suchen, sehen wir, dass ... es viel zu lesen gibt. Aber das Wort "Dekorateur" ist ein Link zum Glossar , das uns sagt:
Dekorateur
Eine Funktion, die eine andere Funktion zurückgibt und normalerweise als Funktionstransformation unter Verwendung der
@wrapper
Syntax angewendet wird . Übliche Beispiele für Dekorateure sindclassmethod()
undstaticmethod()
.Die Decorator-Syntax ist lediglich syntaktischer Zucker. Die folgenden zwei Funktionsdefinitionen sind semantisch äquivalent:
def f(...): ... f = staticmethod(f) @staticmethod def f(...): ...
Das gleiche Konzept existiert für Klassen, wird dort jedoch weniger häufig verwendet. Weitere Informationen zu Dekoratoren finden Sie in der Dokumentation zu Funktionsdefinitionen und Klassendefinitionen.
Das sehen wir also
@foo
def bar():
pass
ist semantisch dasselbe wie:
def bar():
pass
bar = foo(bar)
Sie sind nicht genau gleich, da Python den foo-Ausdruck (der eine gepunktete Suche und ein Funktionsaufruf sein kann) vor dem Balken mit der decorator ( @
) -Syntax auswertet, im anderen Fall jedoch den foo-Ausdruck nach dem Balken auswertet .
(Wenn dieser Unterschied die Bedeutung Ihres Codes beeinflusst, sollten Sie überlegen, was Sie mit Ihrem Leben machen, da dies pathologisch wäre.)
Wenn wir zur Dokumentation der Funktionsdefinitionssyntax zurückkehren, sehen wir:
@f1(arg) @f2 def func(): pass
ist ungefähr gleichbedeutend mit
def func(): pass func = f1(arg)(f2(func))
Dies ist eine Demonstration, dass wir eine Funktion, die zuerst ein Dekorateur ist, sowie Stapeldekoratoren aufrufen können. Funktionen in Python sind erstklassige Objekte. Dies bedeutet, dass Sie eine Funktion als Argument an eine andere Funktion übergeben und Funktionen zurückgeben können. Dekorateure machen beides.
Wenn wir Dekoratoren stapeln, wird die definierte Funktion zuerst an den Dekorator unmittelbar darüber übergeben, dann an den nächsten und so weiter.
Das fasst die Verwendung @
im Kontext von Dekorateuren zusammen.
@
Im Abschnitt zur lexikalischen Analyse der Sprachreferenz haben wir einen Abschnitt zu Operatoren , der Folgendes enthält @
, wodurch es auch zu einem Operator wird:
Die folgenden Token sind Operatoren:
+ - * ** / // % @ << >> & | ^ ~ < > <= >= == !=
und auf der nächsten Seite, dem Datenmodell, haben wir den Abschnitt Emulieren numerischer Typen ,
object.__add__(self, other) object.__sub__(self, other) object.__mul__(self, other) object.__matmul__(self, other) object.__truediv__(self, other) object.__floordiv__(self, other)
[...] Diese Methoden aufgerufen , um die binären arithmetischen Operationen zu implementieren (
+
,-
,*
,@
,/
,//
, [...]
Und wir sehen, dass das __matmul__
entspricht @
. Wenn wir die Dokumentation nach "matmul" durchsuchen, erhalten wir einen Link zu den Neuerungen in Python 3.5 mit "matmul" unter der Überschrift "PEP 465 - Ein dedizierter Infix-Operator für die Matrixmultiplikation".
es kann durch die Definition umgesetzt werden
__matmul__()
,__rmatmul__()
und__imatmul__()
für die regelmäßige, reflektiert und an Ort und Stelle Matrixmultiplikation.
(Jetzt erfahren wir also, dass dies @=
die In-Place-Version ist.) Es erklärt weiter:
Die Matrixmultiplikation ist in vielen Bereichen der Mathematik, Naturwissenschaften und Ingenieurwissenschaften eine besonders häufige Operation, und das Hinzufügen von @ ermöglicht das Schreiben von sauberem Code:
S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)
anstatt:
S = dot((dot(H, beta) - r).T, dot(inv(dot(dot(H, V), H.T)), dot(H, beta) - r))
Während dieser Operator für fast alles überladen werden kann numpy
, würden wir beispielsweise diese Syntax verwenden, um das innere und äußere Produkt von Arrays und Matrizen zu berechnen:
>>> 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]])
@=
Bei der Untersuchung der vorherigen Verwendung stellen wir fest, dass es auch die Inplace-Matrix-Multiplikation gibt. Wenn wir versuchen, es zu verwenden, stellen wir möglicherweise fest, dass es für numpy noch nicht implementiert ist:
>>> 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'.
Wenn es implementiert ist, würde ich erwarten, dass das Ergebnis so aussieht:
>>> m = matrix([1,2,3])
>>> m @= m.T
>>> m
matrix([[14]])
Was macht das "at" (@) -Symbol in Python?
@ Symbol ist ein syntaktischer Zucker Python zu nutzen bietet decorator
, um
die Frage zu paraphrasieren, über Es ist genau das, was tut Dekorateur in Python tun?
Vereinfacht ausgedrückt decorator
können Sie die Definition einer bestimmten Funktion ändern, ohne das Innerste zu berühren (das Schließen).
Dies ist der häufigste Fall, wenn Sie ein wunderbares Paket von einem Drittanbieter importieren. Sie können es visualisieren, Sie können es verwenden, aber Sie können sein Innerstes und sein Herz nicht berühren.
Hier ein kurzes Beispiel:
Angenommen, ich definiere eine read_a_book
Funktion in 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: '
Sie sehen, ich habe vergessen, einen Namen hinzuzufügen.
Wie kann man ein solches Problem lösen? Natürlich könnte ich die Funktion neu definieren als:
def read_a_book():
return "I am reading the book: 'Python Cookbook'"
Was aber, wenn ich die ursprüngliche Funktion nicht manipulieren darf oder wenn Tausende solcher Funktionen bearbeitet werden müssen?
Lösen Sie das Problem, indem Sie anders denken und eine neue Funktion definieren
def add_a_book(func):
def wrapper():
return func() + "Python Cookbook"
return wrapper
Dann setzen Sie es ein.
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, siehst du, ich habe es geändert, read_a_book
ohne es zu berühren. Nichts hält mich davon ab decorator
.
Was ist mit @
@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
ist eine ausgefallene und praktische Art zu sagen read_a_book = add_a_book(read_a_book)
, es ist ein syntaktischer Zucker, nichts ist schicker daran.
Wenn Sie sich auf Code in einem Python-Notizbuch beziehen, das die Numpy- Bibliothek verwendet, @ operator
bedeutet dies Matrix-Multiplikation . Zum Beispiel:
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
Ab Python 3.5 wird das '@' als dediziertes Infix-Symbol für MATRIX MULTIPLICATION verwendet (PEP 0465 - siehe https://www.python.org/dev/peps/pep-0465/ ).
In Python wurden Dekoratoren hinzugefügt, um das Umschließen von Funktionen und Methoden (eine Funktion, die eine Funktion empfängt und eine erweiterte zurückgibt) einfacher zu lesen und zu verstehen. Der ursprüngliche Anwendungsfall bestand darin, die Methoden als Klassenmethoden oder statische Methoden am Kopf ihrer Definition definieren zu können. Ohne die Decorator-Syntax wäre eine eher spärliche und sich wiederholende Definition erforderlich:
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)
Wenn die Decorator-Syntax für denselben Zweck verwendet wird, ist der Code kürzer und leichter zu verstehen:
class WithDecorators:
@staticmethod
def some_static_method():
print("this is static method")
@classmethod
def some_class_method(cls):
print("this is class method")
Allgemeine Syntax und mögliche Implementierungen
Der Dekorator ist im Allgemeinen ein benanntes Objekt ( Lambda-Ausdrücke sind nicht zulässig ), das beim Aufruf ein einzelnes Argument akzeptiert (dies ist die dekorierte Funktion) und ein anderes aufrufbares Objekt zurückgibt. "Callable" wird hier anstelle von "function" mit Vorsatz verwendet. Während Dekorateure häufig im Rahmen von Methoden und Funktionen diskutiert werden, sind sie nicht auf diese beschränkt. Tatsächlich kann alles, was aufrufbar ist (jedes Objekt, das die _call__-Methode implementiert, als aufrufbar betrachtet wird), als Dekorator verwendet werden, und häufig sind von ihnen zurückgegebene Objekte keine einfachen Funktionen, sondern mehr Instanzen komplexerer Klassen, die ihre eigene __call_-Methode implementieren.
Die Dekorationssyntax ist einfach nur ein syntaktischer Zucker . Betrachten Sie die folgende Verwendung des Dekorateurs:
@some_decorator
def decorated_function():
pass
Dies kann immer durch einen expliziten Aufruf des Dekorateurs und eine Neuzuweisung der Funktionen ersetzt werden:
def decorated_function():
pass
decorated_function = some_decorator(decorated_function)
Letzteres ist jedoch weniger lesbar und auch sehr schwer zu verstehen, wenn mehrere Dekorateure für eine einzelne Funktion verwendet werden. Dekorateure können auf verschiedene Arten verwendet werden, wie unten gezeigt:
Als eine Funktion
Es gibt viele Möglichkeiten, benutzerdefinierte Dekoratoren zu schreiben. Am einfachsten ist es jedoch, eine Funktion zu schreiben, die eine Unterfunktion zurückgibt, die den ursprünglichen Funktionsaufruf umschließt.
Die generischen Muster sind wie folgt:
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
Als eine Klasse
Während Dekorateure fast immer mithilfe von Funktionen implementiert werden können, gibt es Situationen, in denen die Verwendung benutzerdefinierter Klassen eine bessere Option ist. Dies ist häufig der Fall, wenn der Dekorateur eine komplexe Parametrisierung benötigt oder von einem bestimmten Zustand abhängt.
Das generische Muster für einen nicht parametrisierten Dekorateur als Klasse lautet wie folgt:
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
Dekorateure parametrisieren
In echtem Code müssen häufig Dekoratoren verwendet werden, die parametrisiert werden können. Wenn die Funktion als Dekorateur verwendet wird, ist die Lösung einfach: Es muss eine zweite Umhüllungsstufe verwendet werden. Hier ist ein einfaches Beispiel für den Dekorateur, der die Ausführung einer dekorierten Funktion bei jedem Aufruf so oft wiederholt:
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
Der so definierte Dekorateur kann folgende Parameter akzeptieren:
>>> @repeat(2)
... def foo():
... print("foo")
...
>>> foo()
foo
foo
Beachten Sie, dass selbst wenn der parametrisierte Dekorator Standardwerte für seine Argumente hat, die Klammern nach seinem Namen erforderlich sind. Der korrekte Weg, den vorhergehenden Dekorator mit Standardargumenten zu verwenden, ist wie folgt:
>>> @repeat()
... def bar():
... print("bar")
...
>>> bar()
bar
bar
bar
Zum Schluss sehen wir Dekorateure mit Eigenschaften.
Eigenschaften
Die Eigenschaften bieten einen integrierten Deskriptortyp , der weiß, wie ein Attribut mit einer Reihe von Methoden verknüpft wird. Eine Eigenschaft akzeptiert vier optionale Argumente: fget, fset, fdel und doc. Die letzte kann bereitgestellt werden, um eine Dokumentzeichenfolge zu definieren, die mit dem Attribut verknüpft ist, als wäre es eine Methode. Hier ist ein Beispiel für eine Rechteckklasse, die entweder durch direkten Zugriff auf Attribute, in denen zwei Eckpunkte gespeichert sind, oder mithilfe der Eigenschaften width und height gesteuert werden kann:
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
)
Die beste Syntax zum Erstellen von Eigenschaften ist die Verwendung von Eigenschaften als Dekorator. Dadurch wird die Anzahl der Methodensignaturen innerhalb der Klasse verringert und der Code lesbarer und wartbarer . Mit Dekorateuren wird die obige Klasse:
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
Um zu sagen, was andere anders haben: Ja, es ist ein Dekorateur.
In Python ist es wie:
Dies kann für alle Arten von nützlichen Dingen verwendet werden, die ermöglicht werden, weil Funktionen Objekte sind und nur notwendige Anweisungen erforderlich sind.
Es zeigt an, dass Sie einen Dekorateur verwenden. Hier ist das Beispiel von Bruce Eckel aus dem Jahr 2008.