PEP 3107 – Funktionsannotationen
- Autor:
- Collin Winter <collinwinter at google.com>, Tony Lownds <tony at lownds.com>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 02. Dez 2006
- Python-Version:
- 3.0
- Post-History:
Zusammenfassung
Diese PEP führt eine Syntax zur Hinzufügung beliebiger Metadatenannotationen zu Python-Funktionen ein [1].
Begründung
Da die 2.x-Serie von Python keine standardisierte Möglichkeit zur Annotation von Funktionsparametern und Rückgabewerten bietet, sind eine Vielzahl von Werkzeugen und Bibliotheken erschienen, um diese Lücke zu füllen. Einige nutzen die in PEP 318 eingeführten Dekoratoren, während andere die Docstrings einer Funktion parsen und dort nach Annotationen suchen.
Diese PEP zielt darauf ab, eine einzige, standardisierte Methode zur Angabe dieser Informationen bereitzustellen und die Verwirrung zu reduzieren, die durch die bisherige Vielfalt an Mechanismen und Syntax verursacht wurde.
Grundlagen von Funktionsannotationen
Bevor wir uns den genauen Einzelheiten der Funktionsannotationen in Python 3.0 zuwenden, sprechen wir zunächst allgemein darüber, was Annotationen sind und was nicht.
- Funktionsannotationen, sowohl für Parameter als auch für Rückgabewerte, sind vollständig optional.
- Funktionsannotationen sind nichts weiter als eine Möglichkeit, beliebige Python-Ausdrücke zur Kompilierzeit mit verschiedenen Teilen einer Funktion zu verknüpfen.
Für sich genommen weist Python Annotationen keine besondere Bedeutung oder Wichtigkeit zu. Wenn sie sich selbst überlassen werden, macht Python diese Ausdrücke lediglich verfügbar, wie in Zugriff auf Funktionsannotationen unten beschrieben.
Die einzige Möglichkeit, wie Annotationen Bedeutung erlangen, ist, wenn sie von Drittanbieter-Bibliotheken interpretiert werden. Diese Annotationskonsumenten können mit den Annotationen einer Funktion tun, was immer sie wollen. Beispielsweise könnte eine Bibliothek stringbasierte Annotationen verwenden, um verbesserte Hilfen bereitzustellen, wie hier:
def compile(source: "something compilable", filename: "where the compilable thing comes from", mode: "is this a single statement or a suite?"): ...
Eine andere Bibliothek könnte verwendet werden, um die Typüberprüfung für Python-Funktionen und -Methoden durchzuführen. Diese Bibliothek könnte Annotationen verwenden, um die erwarteten Eingabe- und Rückgabetypen der Funktion anzugeben, möglicherweise etwas wie:
def haul(item: Haulable, *vargs: PackAnimal) -> Distance: ...
Weder die Zeichenketten im ersten Beispiel noch die Typinformationen im zweiten Beispiel haben jedoch für sich allein eine Bedeutung; die Bedeutung kommt allein von Drittanbieter-Bibliotheken.
- Aus Punkt 2 folgend versucht diese PEP nicht, irgendeine Art von standardisierter Semantik einzuführen, selbst für die eingebauten Typen. Diese Arbeit bleibt Drittanbieter-Bibliotheken vorbehalten.
Syntax
Parameter
Annotationen für Parameter haben die Form von optionalen Ausdrücken, die dem Parameternamen folgen.
def foo(a: expression, b: expression = 5):
...
In Pseudogrammatik sehen Parameter nun so aus: identifier [: expression] [= expression]. Das heißt, Annotationen stehen immer vor dem Standardwert eines Parameters, und sowohl Annotationen als auch Standardwerte sind optional. So wie Gleichheitszeichen verwendet werden, um einen Standardwert anzuzeigen, werden Doppelpunkte verwendet, um Annotationen zu kennzeichnen. Alle Annotationsausdrücke werden bei der Ausführung der Funktionsdefinition ausgewertet, genau wie Standardwerte.
Annotationen für zusätzliche Parameter (d. h. *args und **kwargs) werden ähnlich angezeigt:
def foo(*args: expression, **kwargs: expression):
...
Annotationen für verschachtelte Parameter folgen immer dem Namen des Parameters, nicht der letzten Klammer. Die Annotation aller Parameter eines verschachtelten Parameters ist nicht erforderlich.
def foo((x1, y1: expression),
(x2: expression, y2: expression)=(None, None)):
...
Rückgabewerte
Die bisherigen Beispiele haben Beispiele für die Annotation des Typs des Rückgabewerts einer Funktion ausgelassen. Dies geschieht wie folgt:
def sum() -> expression:
...
Das heißt, der Parameterliste kann nun ein Literal -> und ein Python-Ausdruck folgen. Wie die Annotationen für Parameter wird auch dieser Ausdruck bei der Ausführung der Funktionsdefinition ausgewertet.
Die Grammatik für Funktionsdefinitionen [11] lautet nun:
decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
funcdef: [decorators] 'def' NAME parameters ['->' test] ':' suite
parameters: '(' [typedargslist] ')'
typedargslist: ((tfpdef ['=' test] ',')*
('*' [tname] (',' tname ['=' test])* [',' '**' tname]
| '**' tname)
| tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
tname: NAME [':' test]
tfpdef: tname | '(' tfplist ')'
tfplist: tfpdef (',' tfpdef)* [',']
Lambda
lambda's Syntax unterstützt keine Annotationen. Die Syntax von lambda könnte geändert werden, um Annotationen zu unterstützen, indem Klammern um die Parameterliste erforderlich sind. Es wurde jedoch entschieden [12], diese Änderung nicht vorzunehmen, weil:
- Es wäre eine inkompatible Änderung.
- Lambdas sind sowieso kastriert.
- Das Lambda kann immer in eine Funktion geändert werden.
Zugriff auf Funktionsannotationen
Nach der Kompilierung sind die Annotationen einer Funktion über das Attribut __annotations__ der Funktion verfügbar. Dieses Attribut ist ein veränderbares Wörterbuch, das Parameternamen einem Objekt zuordnet, das den ausgewerteten Annotationsausdruck darstellt.
Es gibt einen speziellen Schlüssel im __annotations__-Mapping, "return". Dieser Schlüssel ist nur vorhanden, wenn für den Rückgabewert der Funktion eine Annotation angegeben wurde.
Zum Beispiel die folgende Annotation
def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):
...
würde zu einem __annotations__-Mapping von führen:
{'a': 'x',
'b': 11,
'c': list,
'return': 9}
Der Schlüssel return wurde gewählt, da er nicht mit dem Namen eines Parameters kollidieren kann; jeder Versuch, return als Parameternamen zu verwenden, würde zu einem SyntaxError führen.
__annotations__ ist ein leeres, veränderbares Wörterbuch, wenn keine Annotationen an der Funktion vorhanden sind oder wenn die Funktion aus einem lambda-Ausdruck erstellt wurde.
Anwendungsfälle
Im Laufe der Diskussion über Annotationen sind eine Reihe von Anwendungsfällen aufgetreten. Einige davon werden hier dargestellt, gruppiert nach der Art der Informationen, die sie vermitteln. Ebenfalls enthalten sind Beispiele für bestehende Produkte und Pakete, die Annotationen nutzen könnten.
- Bereitstellung von Typinformationen
- Andere Informationen
- Dokumentation für Parameter und Rückgabewerte ([23])
Standardbibliothek
pydoc und inspect
Das pydoc-Modul sollte die Funktionsannotationen anzeigen, wenn es Hilfe für eine Funktion ausgibt. Das inspect-Modul sollte geändert werden, um Annotationen zu unterstützen.
Bezug zu anderen PEPs
Funktionssignaturobjekte (PEP 362)
Funktionssignaturobjekte sollten die Annotationen der Funktion preisgeben. Das Parameter-Objekt kann geändert werden oder andere Änderungen können angebracht sein.
Implementierung
Eine Referenzimplementierung wurde in den py3k-Zweig (früher "p3yk") mit der Revisionsnummer 53170 eingecheckt [10].
Abgelehnte Vorschläge
- Der BDFL lehnte die Idee des Autors für eine spezielle Syntax zur Hinzufügung von Annotationen zu Generatoren als "zu hässlich" ab [2].
- Obwohl früh diskutiert ([5], [6]), wurde die Aufnahme spezieller Objekte in die Standardbibliothek zur Annotation von Generatorfunktionen und höherwertigen Funktionen letztendlich als für Drittanbieter-Bibliotheken geeigneter abgelehnt; die Aufnahme in die Standardbibliothek warf zu viele knifflige Probleme auf.
- Trotz erheblicher Diskussionen über eine standardisierte Syntax für Typparametrisierung wurde entschieden, dass dies ebenfalls Drittanbieter-Bibliotheken überlassen werden sollte. ([7], [8], [9]).
- Trotz weiterer Diskussionen wurde entschieden, keinen Mechanismus für die Interoperabilität von Annotationen zu standardisieren. Die Standardisierung von Interoperabilitätskonventionen zu diesem Zeitpunkt wäre verfrüht. Wir möchten, dass sich diese Konventionen organisch entwickeln, basierend auf realer Nutzung und Notwendigkeit, anstatt alle Benutzer in ein konstruiertes Schema zu zwingen. ([13], [14], [15]).
Referenzen und Fußnoten
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-3107.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT