Was macht das "at" (@) -Symbol in Python?


579

Ich schaue mir einen Python-Code an, der das @Symbol verwendet, aber ich habe keine Ahnung, was er tut. 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.

Antworten:


303

Ein @Symbol am Anfang einer Zeile ist für die Klasse, die Funktion und Verfahren verwendet Dekoratore .

Lesen Sie hier mehr:

PEP 318: Dekorateure

Python-Dekorateure

Die häufigsten Python-Dekoratoren, denen Sie begegnen werden, sind:

@Eigentum

@classmethod

@staticmethod

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 @.


31
Es sieht so aus, als ob es auch ein Matrixmultiplikationsoperator sein kann: stackoverflow.com/a/21563036/5049813
Pro Q

@decorators können auch hinzugefügt werden
Vijay Panchal

348

Beispiel

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 classSie nach einem Dekorateur definieren, im Grunde genommen als / argumentan das function/ methodunmittelbar nach dem @Zeichen weitergegeben wird.

Erste Sichtung

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.


7
Im Fall von Flasks app.route("/"): Diese Funktion gibt eine Funktion zurück, die Sie mit Ihrem hello()als Argument
aufrufen

3
Was ist der syntaktische oder praktische Vorteil, wenn Dekorateure hier sind, anstatt (zum Beispiel) einfach so etwas wie app.route("/", hello)unmittelbar nach dem Definieren aufzurufen hellooder sogar helloals Lambda in den Argumenten zu definieren app.route? (Das letztere Beispiel ist bei Node.js- http.Serverund Express-Routen üblich.)
iono

185

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.


1
In dieser Zeile s "ome_func = decorator (some_func)" ist die erste some_func eine Variable = für die Funktion some_func, richtig?
Viragos

147

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

14
Sie haben auch den @=Operator (an Ort und Stelle) __imatmul__.
Pål GD

Gibt es andere überschreibbare Operatoren wie diesen? Ich kenne __add__und bin __sub__mit + bzw. - verbunden, habe aber noch nie von dem @Zeichen 1 gehört. Lauern da draußen noch andere?
Thomas Kimber

103

Was macht das "at" (@) -Symbol in Python?

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 @ baufruft a.__matmul__(b)- so dass diese Syntax:

a @ b

gleichwertig

dot(a, b)

und

a @= b

gleichwertig

a = dot(a, b)

wo dotist zum Beispiel die Numpy-Matrix-Multiplikationsfunktion und aund bsind Matrizen.

Wie können Sie das selbst entdecken?

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:

  • Dekorateure
  • ein Operator zwischen Faktoren
  • ein erweiterter Zuweisungsoperator

Decorator-Syntax:

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 @wrapperSyntax angewendet wird . Übliche Beispiele für Dekorateure sind classmethod()und staticmethod().

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

Gestapelte Dekorateure

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.

Der Betreiber, @

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

Inplace-Matrixmultiplikation: @=

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

36

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 decoratorkö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_bookFunktion 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_bookohne 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_bookist 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.


16

Wenn Sie sich auf Code in einem Python-Notizbuch beziehen, das die Numpy- Bibliothek verwendet, @ operatorbedeutet 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


6

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

2

Um zu sagen, was andere anders haben: Ja, es ist ein Dekorateur.

In Python ist es wie:

  1. Erstellen einer Funktion (folgt unter dem @ Aufruf)
  2. Aufrufen einer anderen Funktion, um Ihre erstellte Funktion auszuführen. Dies gibt eine neue Funktion zurück. Die von Ihnen aufgerufene Funktion ist das Argument des @.
  3. Ersetzen der definierten Funktion durch die zurückgegebene neue Funktion.

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.


2

Das @ -Symbol wird auch verwendet, um auf Variablen in einer Plydata / Pandas-Datenrahmenabfrage zuzugreifen pandas.DataFrame.query. Beispiel:

df = pandas.DataFrame({'foo': [1,2,15,17]})
y = 10
df >> query('foo > @y') # plydata
df.query('foo > @y') # pandas

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.