958 Stimmen

Welche Funktion hat das Symbol "at" (@) in Python?

Was bedeutet die @ Symbol in Python tun?

95voto

AbstProcDo Punkte 17041

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.

26voto

f__society Punkte 474

Wenn Sie sich auf einen Code in einem Python-Notebook beziehen, der die Numpy Bibliothek, dann @ operator bedeutet 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

15voto

iun1x Punkte 923

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

13voto

dpodbori Punkte 289

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/ )

7voto

Mohammed Jubayer Punkte 1564

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X