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@testzu ä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@testdie 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.
Urheberrecht
Dieses Dokument wird in die Public Domain oder unter die CC0-1.0-Universal-Lizenz gestellt, je nachdem, welche Lizenz permissiver ist.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0614.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT