Was bedeutet die @
Symbol in Python tun?
Antworten
Zu viele Anzeigen?Welche Funktion hat das Symbol "at" (@) in Python?
@ Symbol ist ein syntaktischer Zucker, den Python zur Verfügung stellt, um die decorator
,
um die Frage zu paraphrasieren: Es geht genau darum, was Decorator in Python macht?
Einfach ausgedrückt decorator
ermöglichen es Ihnen, die Definition einer bestimmten Funktion zu ändern, ohne ihr Innerstes (ihren Abschluss) zu berühren.
Das ist vor allem dann der Fall, wenn Sie ein wunderbares Paket von einem Drittanbieter importieren. Sie können es visualisieren, Sie können es benutzen, aber Sie können sein Innerstes und sein Herz nicht berühren.
Hier ist ein kurzes Beispiel,
Angenommen, ich definiere eine read_a_book
Funktion auf 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: '
Ich habe nämlich vergessen, einen Namen hinzuzufügen.
Wie lässt sich ein solches Problem lösen? Natürlich könnte ich die Funktion umdefinieren als:
def read_a_book():
return "I am reading the book: 'Python Cookbook'"
Aber was ist, wenn ich die ursprüngliche Funktion nicht manipulieren darf oder wenn es Tausende solcher Funktionen gibt, die bearbeitet werden müssen?
Lösen Sie das Problem, indem Sie anders denken und eine new_function 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 die read_a_book
ohne seinen inneren Verschluss zu berühren. Nichts hält mich auf, ausgestattet mit decorator
.
Worum geht's @
@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 schicke und praktische Art zu sagen read_a_book = add_a_book(read_a_book)
Es handelt sich um einen syntaktischen Zucker, der nicht weiter ausgefeilt ist.
In Python wurden Dekoratoren hinzugefügt, um die Umhüllung von Funktionen und Methoden (eine Funktion, die eine Funktion empfängt und eine erweiterte Funktion zurückgibt) einfacher zu lesen und zu verstehen. Der ursprüngliche Anwendungsfall war die Möglichkeit, die Methoden als Klassenmethoden oder statische Methoden im Kopf ihrer Definition zu definieren. Ohne die Decorator-Syntax würde dies eine eher spärliche und sich wiederholende Definition erfordern:
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 Dekorator-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 erlaubt ), die beim Aufruf ein einziges Argument akzeptiert (es wird die dekorierte Funktion sein) und ein anderes aufrufbares Objekt zurückgibt. "Aufrufbar" wird hier mit Absicht anstelle von "Funktion" verwendet. Während Dekoratoren oft 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, wird als aufrufbar betrachtet), als Dekorator verwendet werden, und oft sind die von ihnen zurückgegebenen Objekte keine einfachen Funktionen, sondern Instanzen von komplexeren Klassen, die ihre eigene __call_-Methode implementieren.
Die Syntax des Dekors ist einfach nur ein syntaktischer Zucker . Betrachten Sie die folgende Verwendung des Dekorators:
@some_decorator
def decorated_function():
pass
Dies kann immer durch einen expliziten Dekoratoraufruf und eine Funktionsneuzuweisung 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 Dekoratoren für eine einzige Funktion verwendet werden. Dekoratoren können auf mehrere verschiedene Arten verwendet werden, wie unten gezeigt:
Als Funktion
Es gibt viele Möglichkeiten, benutzerdefinierte Dekoratoren zu schreiben, aber der einfachste Weg ist, eine Funktion zu schreiben, die eine Unterfunktion zurückgibt, die den ursprünglichen Funktionsaufruf umschließt.
Das allgemeine Muster sieht folgendermaßen aus:
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 Klasse
Während Dekoratoren fast immer mit Funktionen implementiert werden können, gibt es einige Situationen, in denen die Verwendung von benutzerdefinierten Klassen eine bessere Option ist. Dies ist oft der Fall, wenn der Dekorator eine komplexe Parametrisierung benötigt oder von einem bestimmten Zustand abhängt.
Das generische Muster für einen nicht-parametrisierten Dekorator als Klasse ist 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
Parametrisierung von Dekorateuren
In echtem Code besteht oft die Notwendigkeit, Dekoratoren zu verwenden, die parametrisiert werden können. Wenn die Funktion als Dekorator verwendet wird, ist die Lösung einfach - es muss eine zweite Ebene der Umhüllung verwendet werden. Hier ist ein einfaches Beispiel für einen Dekorator, der die Ausführung einer dekorierten Funktion bei jedem Aufruf die angegebene Anzahl von Malen 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 auf diese Weise definierte Dekorator kann 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 um den vorhergehenden Dekorator mit Standardargumenten zu verwenden ist wie folgt:
>>> @repeat()
... def bar():
... print("bar")
...
>>> bar()
bar
bar
bar
Schließlich sehen wir uns Dekoratoren mit Eigenschaften an.
Eigenschaften
Die Eigenschaften bieten eine eingebaute Deskriptor Typ, der weiß, wie man ein Attribut mit einer Reihe von Methoden verknüpft. Eine Eigenschaft hat vier optionale Argumente: fget , fset , fdel und doc . Das letzte Argument kann angegeben werden, um einen Docstring zu definieren, der mit dem Attribut verknüpft wird, als wäre es eine Methode. Hier ist ein Beispiel für eine Rectangle-Klasse, die entweder durch direkten Zugriff auf Attribute, die zwei Eckpunkte speichern, oder durch die Verwendung 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 für die Erstellung von Eigenschaften ist die Verwendung von property als Dekorator. Dies wird die Anzahl der Methodensignaturen zu reduzieren innerhalb der Klasse und machen Code mehr lesbar und wartbar . 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
Ab Python 3.5 wird das '@' als eigenes Infix-Symbol für MATRIX-Multiplikation verwendet (PEP 0465 -- siehe https://www.python.org/dev/peps/pep-0465/ )
Der Python-Dekorator ist wie ein Wrapper für eine Funktion oder eine Klasse. Es ist immer noch zu konzeptionell.
def function_decorator(func):
def wrapped_func():
# Do something before the function is executed
func()
# Do something after the function has been executed
return wrapped_func
Der obige Code ist eine Definition für einen Dekorator, der eine Funktion dekoriert. function_decorator ist der Name des Dekorators.
umhüllte_func ist der Name der inneren Funktion, die eigentlich nur in dieser Dekoratordefinition verwendet wird. func ist die Funktion, die dekoriert wird. In der inneren Funktion umhüllte_func können wir alles tun, was vor und nach der func genannt wird. Nachdem der Dekorator definiert ist, verwenden wir ihn einfach wie folgt.
@function_decorator
def func():
pass
Wenn wir dann die Funktion func werden die Verhaltensweisen, die wir im Dekorator definiert haben, ebenfalls ausgeführt.
BEISPIEL :
from functools import wraps
def mydecorator(f):
@wraps(f)
def wrapped(*args, **kwargs):
print "Before decorated function"
r = f(*args, **kwargs)
print "After decorated function"
return r
return wrapped
@mydecorator
def myfunc(myarg):
print "my function", myarg
return "return value"
r = myfunc('asdf')
print r
Ausgang :
Before decorated function
my function asdf
After decorated function
return value