Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 614 – Lockerung der Grammatikbeschränkungen für Decorators

Autor:
Brandt Bucher <brandt at python.org>
Sponsor:
Guido van Rossum <guido at python.org>
Status:
Final
Typ:
Standards Track
Erstellt:
10. Feb. 2020
Python-Version:
3.9
Post-History:
11. Feb. 2020, 18. Feb. 2020, 03. Mär. 2020
Resolution:
Python-Dev thread

Inhaltsverzeichnis

Zusammenfassung

Python verlangt derzeit, dass alle Decorators aus einem gepunkteten Namen bestehen, optional gefolgt von einem einzigen Aufruf. Dieses PEP schlägt vor, diese Einschränkungen aufzuheben und Decorators als beliebige gültige Ausdrücke zuzulassen.

Motivation

Als Decorators eingeführt wurden, beschrieb Guido die Motivation, ihre Syntax einzuschränken, als Präferenz, nicht als technische Anforderung.

Ich habe hier ein Bauchgefühl. Ich weiß nicht, woher es kommt, aber ich habe es... Also, obwohl es recht einfach wäre, die Syntax in Zukunft auf @test zu ändern, möchte ich bei der stärker eingeschränkten Form bleiben, es sei denn, es wird ein realer Anwendungsfall präsentiert, bei dem die Zulassung von @test die Lesbarkeit erhöhen würde.

Obwohl diese Einschränkungen in der Praxis selten auftraten, sind BPO-Probleme und Mailinglistenbeiträge im Laufe der Jahre konstant aufgetaucht, die eine Aufhebung forderten. Der jüngste davon (der dieses Proposal anstieß) enthielt ein gutes Beispiel für Code, der die PyQt5-Bibliothek verwendet, welcher lesbarer, idiomatischer und wartbarer würde, wenn die bestehenden Einschränkungen gelockert würden. Leicht modifiziert.

buttons = [QPushButton(f'Button {i}') for i in range(10)]

# Do stuff with the list of buttons...

@buttons[0].clicked.connect
def spam():
    ...

@buttons[1].clicked.connect
def eggs():
    ...

# Do stuff with the list of buttons...

Derzeit müssen diese Decorators wie folgt umgeschrieben werden:

button_0 = buttons[0]

@button_0.clicked.connect
def spam():
    ...

button_1 = buttons[1]

@button_1.clicked.connect
def eggs():
    ...

Darüber hinaus ist die aktuelle Grammatik bereits locker genug, dass es trivial ist, kompliziertere Decorator-Ausdrücke zusammenzuflicken. Anstatt also willkürlich komplexe Ausdrücke zuzulassen, wie beabsichtigt, machen die aktuellen Einschränkungen sie nur hässlicher und weniger effizient.

# Identity function hack:

def _(x):
    return x

@_(buttons[0].clicked.connect)
def spam():
    ...

# eval hack:

@eval("buttons[1].clicked.connect")
def eggs():
    ...

Begründung

Erlauben beliebiger Ausdrücke

Die Entscheidung, *beliebige* gültige Ausdrücke zuzulassen (und nicht nur die aktuellen Einschränkungen zu lockern, um beispielsweise Subskriptionen zuzulassen), wird schon seit einiger Zeit als nächster logischer Schritt in der Evolution der Decorator-Grammatik betrachtet. Wie Guido in einem weiteren Mailinglisten-Thread bemerkte:

Ich halte es nicht für vernünftig, es weniger einzuschränken als es derzeit der Fall ist, aber mehr als einen allgemeinen Ausdruck.

Die Sonderbehandlung der Grammatik, um *einige* nützliche Fälle zuzulassen, würde die aktuelle Situation nur verkomplizieren und mit ziemlicher Sicherheit dazu führen, dass der Prozess sich in Zukunft wiederholt. Darüber hinaus ist ein Zweck dieser grammatikalischen Änderung, der Versuchung zu widerstehen, Hacks wie die oben gezeigten eval- und Identitätsfunktions-Anti-Pattern zu verwenden.

Kurz gesagt: Wenn wir etwas willkürliche Einschränkungen aufheben, sollten wir *alle* aufheben.

Was zählt als „Ausdruck“

Im gesamten Dokument wird das Wort „Ausdruck“ wie in der Python-Sprachreferenz definiert verwendet. Dies kann zusammenfassend als „alles, was als Test in if-, elif- und while-Blöcken gültig ist“ beschrieben werden. Dies unterscheidet sich subtil von einer vielleicht populäreren Definition, die zusammenfassend als „alles, was als String-Eingabe für eval gültig ist“ beschrieben werden kann.

Diese Definition von „Ausdruck“ ist praktisch, da sie gut zu unseren Bedürfnissen passt und die erlaubte Grammatik bestehender Sprachkonstrukte wiederverwendet. Sie hat zwei subtile Unterschiede zur anderen Definition.

Tupel-Literale *müssen* in Klammern gesetzt werden

Dies basiert auf einer Beobachtung, die Guido in derselben E-Mail machte. Fortsetzung direkt von oben.

Ich würde jedoch keine Kommas zulassen – es gibt keinen Weg, dass
@f, g
def pooh(): ...

sinnvoll sein kann.

Tatsächlich könnte es unerfahrene Leser sogar zu dem Schluss kommen lassen, dass mehrere Decorators angewendet werden, als ob sie gestapelt wären. Die Anforderung von Klammern macht hier die (zugegebenermaßen unsinnige) Absicht klar, ohne weitere Einschränkungen und grammatikalische Komplikationen aufzuerlegen.

Benannte Ausdrücke *müssen nicht* in Klammern gesetzt werden

Hier ist die Wahl der Syntax eindeutig. PEP 572 erklärt, warum es Klammern um Top-Level-Ausdrucksanweisungen erfordert.

Diese Regel ist enthalten, um die Wahl für den Benutzer zwischen einer Zuweisungsanweisung und einem Zuweisungsausdruck zu vereinfachen – es gibt keine syntaktische Position, an der beide gültig sind.

Da eine Zuweisungsanweisung hier nicht gültig ist, sollten Zuweisungsausdrücke nicht unnötigerweise mit Klammern belastet werden.

Spezifikation

Die Grammatik für Decorators ist derzeit

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE

Dieses PEP schlägt vor, sie zu vereinfachen auf

decorator: '@' namedexpr_test NEWLINE

Abwärtskompatibilität

Diese neue Grammatik ist vollständig abwärtskompatibel zur bestehenden Grammatik.

Wie man das lehrt

Decorators können weiterhin wie immer gelehrt werden; der durchschnittliche Python-Programmierer ist sich wahrscheinlich nicht bewusst, dass die aktuelle Einschränkung überhaupt existiert.

Referenzimplementierung

Der Autor hat eine CPython-Implementierung geschrieben.


Quelle: https://github.com/python/peps/blob/main/peps/pep-0614.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT