PEP 526 – Syntax for Variable Annotations
- Autor:
- Ryan Gonzalez <rymg19 at gmail.com>, Philip House <phouse512 at gmail.com>, Ivan Levkivskyi <levkivskyi at gmail.com>, Lisa Roach <lisaroach14 at gmail.com>, Guido van Rossum <guido at python.org>
- Status:
- Final
- Typ:
- Standards Track
- Thema:
- Typisierung
- Erstellt:
- 09-Aug-2016
- Python-Version:
- 3.6
- Post-History:
- 30-Aug-2016, 02-Sep-2016
- Resolution:
- Python-Dev Nachricht
Status
Dieses PEP wurde vom BDFL provisorisch angenommen. Sehen Sie die Akzeptanznachricht für mehr Details: https://mail.python.org/pipermail/python-dev/2016-September/146282.html
Hinweis für Gutachter
Dieses PEP wurde in einem separaten Repository erstellt: https://github.com/phouse512/peps/tree/pep-0526.
Es gab eine vorläufige Diskussion auf python-ideas und unter https://github.com/python/typing/issues/258.
Bevor Sie einen Einwand in einem öffentlichen Forum vorbringen, lesen Sie bitte zumindest die Zusammenfassung der abgelehnten Ideen am Ende dieses PEP.
Zusammenfassung
PEP 484 führte Typ-Hints, auch bekannt als Typ-Annotationen, ein. Während sein Hauptaugenmerk auf Funktionsannotationen lag, führte es auch die Idee von Typ-Kommentaren zur Annotation von Variablen ein.
# 'primes' is a list of integers
primes = [] # type: List[int]
# 'captain' is a string (Note: initial value is a problem)
captain = ... # type: str
class Starship:
# 'stats' is a class variable
stats = {} # type: Dict[str, int]
Dieses PEP zielt darauf ab, eine Syntax zu Python hinzuzufügen, um die Typen von Variablen (einschließlich Klassen- und Instanzvariablen) zu annotieren, anstatt sie durch Kommentare auszudrücken.
primes: List[int] = []
captain: str # Note: no initial value!
class Starship:
stats: ClassVar[Dict[str, int]] = {}
PEP 484 besagt ausdrücklich, dass Typ-Kommentare dazu gedacht sind, bei komplexen Fällen bei der Typinferenz zu helfen, und dieses PEP ändert diese Absicht nicht. Da jedoch in der Praxis Typ-Kommentare auch für Klassen- und Instanzvariablen übernommen wurden, erörtert dieses PEP auch die Verwendung von Typ-Annotationen für diese Variablen.
Begründung
Obwohl Typ-Kommentare gut genug funktionieren, hat die Tatsache, dass sie als Kommentare ausgedrückt werden, einige Nachteile:
- Texteditoren heben Kommentare oft anders hervor als Typ-Annotationen.
- Es gibt keine Möglichkeit, den Typ einer undefinierten Variablen zu annotieren; man muss sie mit
Noneinitialisieren (z. B.a = None # type: int). - Variablen, die in einem bedingten Zweig annotiert sind, sind schwer zu lesen.
if some_value: my_var = function() # type: Logger else: my_var = another_function() # Why isn't there a type here?
- Da Typ-Kommentare nicht wirklich Teil der Sprache sind, erfordert es ein benutzerdefiniertes Parsen anstelle der bloßen Verwendung von
ast, wenn ein Python-Skript sie parsen möchte. - Typ-Kommentare werden in typeshed viel verwendet. Die Migration von typeshed zur Verwendung der Variablenannotationssyntax anstelle von Typ-Kommentaren würde die Lesbarkeit von Stubs verbessern.
- In Situationen, in denen normale Kommentare und Typ-Kommentare zusammen verwendet werden, ist es schwierig, sie zu unterscheiden.
path = None # type: Optional[str] # Path to module source
- Es ist unmöglich, die Annotationen zur Laufzeit abzurufen, außer durch den Versuch, den Quellcode des Moduls zu finden und ihn zur Laufzeit zu parsen, was, gelinde gesagt, umständlich ist.
Die Mehrheit dieser Probleme kann durch die Aufnahme der Syntax als Kernbestandteil der Sprache gelöst werden. Darüber hinaus wird eine dedizierte Annotationssyntax für Klassen- und Instanzvariablen (zusätzlich zu Methodenannotationen) den Weg für statisches Duck-Typing als Ergänzung zum nominellen Typen, das durch PEP 484 definiert wird, ebnen.
Non-goals
Obwohl der Vorschlag von einer Erweiterung der Standardbibliotheksfunktion typing.get_type_hints für den Laufzeitabruf von Annotationen begleitet wird, sind Variablenannotationen nicht für die Laufzeit-Typprüfung konzipiert. Drittanbieterpakete müssen entwickelt werden, um eine solche Funktionalität zu implementieren.
Es sollte auch betont werden, dass Python eine dynamisch typisierte Sprache bleibt und die Autoren keinerlei Wunsch haben, Typ-Hints jemals, selbst per Konvention, obligatorisch zu machen. Typ-Annotationen sollten nicht mit Variablendeklarationen in statisch typisierten Sprachen verwechselt werden. Das Ziel der Annotationssyntax ist es, eine einfache Möglichkeit zur Angabe von strukturierten Typ-Metadaten für Drittanbieter-Tools zu bieten.
Dieses PEP schreibt keine Änderungen an den Typüberprüfungsregeln von Typ-Checkern vor. Es bietet lediglich eine besser lesbare Syntax als Ersatz für Typ-Kommentare.
Spezifikation
Typ-Annotationen können einer Zuweisungsanweisung oder einem einzelnen Ausdruck hinzugefügt werden, der den gewünschten Typ des Annotationsziels für einen Drittanbieter-Typ-Checker angibt.
my_var: int
my_var = 5 # Passes type check.
other_var: int = 'a' # Flagged as error by type checker,
# but OK at runtime.
Diese Syntax führt keine neuen Semantiken über PEP 484 hinaus ein, sodass die folgenden drei Anweisungen äquivalent sind:
var = value # type: annotation
var: annotation; var = value
var: annotation = value
Im Folgenden legen wir die Syntax von Typ-Annotationen in verschiedenen Kontexten und deren Laufzeiteffekte dar.
Wir schlagen auch vor, wie Typ-Checker Annotationen interpretieren könnten, aber die Einhaltung dieser Vorschläge ist nicht zwingend. (Dies steht im Einklang mit der Haltung zur Konformität in PEP 484.)
Globale und lokale Variablentypen
Die Typen von Locals und Globals können wie folgt annotiert werden:
some_number: int # variable without initial value
some_list: List[int] = [] # variable with initial value
Das Weglassen des Anfangswerts ermöglicht eine einfachere Typisierung von Variablen, die in bedingten Zweigen zugewiesen werden.
sane_world: bool
if 2+2 == 4:
sane_world = True
else:
sane_world = False
Beachten Sie, dass die Syntax zwar Tupelpackung zulässt, aber nicht erlaubt, die Typen von Variablen zu annotieren, wenn Tupelentpackung verwendet wird.
# Tuple packing with variable annotation syntax
t: Tuple[int, ...] = (1, 2, 3)
# or
t: Tuple[int, ...] = 1, 2, 3 # This only works in Python 3.8+
# Tuple unpacking with variable annotation syntax
header: str
kind: int
body: Optional[List[str]]
header, kind, body = message
Das Weglassen des Anfangswerts lässt die Variable uninitialisiert.
a: int
print(a) # raises NameError
Eine lokale Variable zu annotieren führt jedoch dazu, dass der Interpreter sie immer als lokal behandelt.
def f():
a: int
print(a) # raises UnboundLocalError
# Commenting out the a: int makes it a NameError.
als ob der Code wäre
def f():
if False: a = 0
print(a) # raises UnboundLocalError
Duplizierte Typ-Annotationen werden ignoriert. Statische Typ-Checker können jedoch eine Warnung für Annotationen derselben Variable mit unterschiedlichem Typ ausgeben.
a: int
a: str # Static type checker may or may not warn about this.
Klassen- und Instanzvariablentypen
Typ-Annotationen können auch verwendet werden, um Klassen- und Instanzvariablen in Klassenkörpern und Methoden zu annotieren. Insbesondere ermöglicht die wertlose Notation a: int das Annotieren von Instanzvariablen, die in __init__ oder __new__ initialisiert werden sollen. Die vorgeschlagene Syntax lautet:
class BasicStarship:
captain: str = 'Picard' # instance variable with default
damage: int # instance variable without default
stats: ClassVar[Dict[str, int]] = {} # class variable
Hier ist ClassVar eine spezielle Klasse, die vom `typing`-Modul definiert wird und dem statischen Typ-Checker anzeigt, dass diese Variable nicht auf Instanzen gesetzt werden soll.
Beachten Sie, dass ein ClassVar-Parameter keine Typvariablen enthalten kann, unabhängig vom Grad der Verschachtelung: ClassVar[T] und ClassVar[List[Set[T]]] sind beide ungültig, wenn T eine Typvariable ist.
Dies könnte mit einem detaillierteren Beispiel veranschaulicht werden. In dieser Klasse
class Starship:
captain = 'Picard'
stats = {}
def __init__(self, damage, captain=None):
self.damage = damage
if captain:
self.captain = captain # Else keep the default
def hit(self):
Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1
stats ist als Klassenvariable gedacht (um viele verschiedene pro Spiel Statistiken zu verfolgen), während captain eine Instanzvariable mit einem Standardwert ist, der in der Klasse gesetzt ist. Dieser Unterschied wird von einem Typ-Checker möglicherweise nicht erkannt: Beide werden in der Klasse initialisiert, aber captain dient nur als praktischer Standardwert für die Instanzvariable, während stats wirklich eine Klassenvariable ist – sie soll von allen Instanzen geteilt werden.
Da beide Variablen auf Klassenebene initialisiert werden, ist es nützlich, sie durch Kennzeichnung von Klassenvariablen mit Typen, die in ClassVar[...] eingehüllt sind, zu unterscheiden. Auf diese Weise kann ein Typ-Checker versehentliche Zuweisungen zu Attributen mit demselben Namen auf Instanzen melden.
Zum Beispiel die Annotation der diskutierten Klasse
class Starship:
captain: str = 'Picard'
damage: int
stats: ClassVar[Dict[str, int]] = {}
def __init__(self, damage: int, captain: str = None):
self.damage = damage
if captain:
self.captain = captain # Else keep the default
def hit(self):
Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1
enterprise_d = Starship(3000)
enterprise_d.stats = {} # Flagged as error by a type checker
Starship.stats = {} # This is OK
Aus Bequemlichkeit (und Konvention) können Instanzvariablen in __init__ oder anderen Methoden statt in der Klasse annotiert werden.
from typing import Generic, TypeVar
T = TypeVar('T')
class Box(Generic[T]):
def __init__(self, content):
self.content: T = content
Ausdrücke annotieren
Das Ziel der Annotation kann jedes gültige einzelne Zuweisungsziel sein, zumindest syntaktisch (es liegt am Typ-Checker, was er damit macht).
class Cls:
pass
c = Cls()
c.x: int = 0 # Annotates c.x with int.
c.y: int # Annotates c.y with int.
d = {}
d['a']: int = 0 # Annotates d['a'] with int.
d['b']: int # Annotates d['b'] with int.
Beachten Sie, dass selbst ein in Klammern gesetzter Name als Ausdruck und nicht als einfacher Name gilt.
(x): int # Annotates x with int, (x) treated as expression by compiler.
(y): int = 0 # Same situation here.
Wo Annotationen nicht erlaubt sind
Es ist illegal, zu versuchen, Variablen, die im selben Funktionsbereich global oder nonlocal unterliegen, zu annotieren.
def f():
global x: int # SyntaxError
def g():
x: int # Also a SyntaxError
global x
Der Grund dafür ist, dass global und nonlocal keine Variablen besitzen; daher gehören die Typ-Annotationen in den Bereich, der die Variable besitzt.
Nur einzelne Zuweisungsziele und einzelne Werte auf der rechten Seite sind erlaubt. Außerdem kann man keine Variablen annotieren, die in einer for- oder with-Anweisung verwendet werden; sie können im Voraus annotiert werden, ähnlich dem Tupelentpacken.
a: int
for a in my_iter:
...
f: MyFile
with myfunc() as f:
...
Variablenannotationen in Stub-Dateien
Da Variablenannotationen besser lesbar sind als Typ-Kommentare, werden sie in Stub-Dateien für alle Python-Versionen, einschließlich Python 2.7, bevorzugt. Beachten Sie, dass Stub-Dateien nicht von Python-Interpretern ausgeführt werden und daher die Verwendung von Variablenannotationen keine Fehler verursacht. Typ-Checker sollten Variablenannotationen in Stubs für alle Python-Versionen unterstützen. Zum Beispiel:
# file lib.pyi
ADDRESS: unicode = ...
class Error:
cause: Union[str, unicode]
Bevorzugter Programmierstil für Variablenannotationen
Annotationen für Variablen auf Modulebene, Klassen- und Instanzvariablen sowie lokale Variablen sollten ein einzelnes Leerzeichen nach dem entsprechenden Doppelpunkt haben. Es sollte kein Leerzeichen vor dem Doppelpunkt stehen. Wenn eine Zuweisung eine rechte Seite hat, dann sollte das Gleichheitszeichen auf beiden Seiten genau ein Leerzeichen haben. Beispiele:
- Ja
code: int class Point: coords: Tuple[int, int] label: str = '<unknown>'
- Nein
code:int # No space after colon code : int # Space before colon class Test: result: int=0 # No spaces around equality sign
Änderungen an der Standardbibliothek und Dokumentation
- Ein neuer kovarianter Typ
ClassVar[T_co]wird dem Modultypinghinzugefügt. Er akzeptiert nur ein einziges Argument, das ein gültiger Typ sein sollte, und wird verwendet, um Klassenvariablen zu annotieren, die nicht auf Klasseninstanzen gesetzt werden sollen. Diese Einschränkung wird durch statische Checker sichergestellt, aber nicht zur Laufzeit. Siehe den Abschnitt classvar für Beispiele und Erklärungen zur Verwendung vonClassVarund den Abschnitt rejected für weitere Informationen über die Gründe fürClassVar. - Die Funktion
get_type_hintsim Modultypingwird erweitert, so dass man Typ-Annotationen zur Laufzeit von Modulen und Klassen sowie Funktionen abrufen kann. Annotationen werden als Wörterbuch zurückgegeben, das von Variablen oder Argumenten zu ihren Typ-Hints mit ausgewerteten Forward-Referenzen zugeordnet ist. Für Klassen gibt es eine Zuordnung zurück (vielleichtcollections.ChainMap), die aus Annotationen in der Method Resolution Order konstruiert wird. - Empfohlene Richtlinien für die Verwendung von Annotationen werden der Dokumentation hinzugefügt, die eine pädagogische Wiederholung der in diesem PEP und in PEP 484 beschriebenen Spezifikationen enthält. Darüber hinaus wird ein Hilfsskript zum Übersetzen von Typ-Kommentaren in Typ-Annotationen separat von der Standardbibliothek veröffentlicht.
Laufzeiteffekte von Typannotationen
Das Annotieren einer lokalen Variable führt dazu, dass der Interpreter sie als lokal behandelt, auch wenn sie nie zugewiesen wurde. Annotationen für lokale Variablen werden nicht ausgewertet.
def f():
x: NonexistentName # No error.
Wenn es sich jedoch auf Modul- oder Klassenebene befindet, wird der Typ ausgewertet.
x: NonexistentName # Error!
class X:
var: NonexistentName # Error!
Darüber hinaus werden auf Modul- oder Klassenebene, wenn das annotierte Element ein einfacher Name ist, dieses und die Annotation im Attribut __annotations__ dieses Moduls oder dieser Klasse (gemangelt, wenn privat) als geordnete Zuordnung von Namen zu ausgewerteten Annotationen gespeichert. Hier ist ein Beispiel:
from typing import Dict
class Player:
...
players: Dict[str, Player]
__points: int
print(__annotations__)
# prints: {'players': typing.Dict[str, __main__.Player],
# '_Player__points': <class 'int'>}
__annotations__ ist beschreibbar, daher ist dies erlaubt:
__annotations__['s'] = str
Der Versuch, __annotations__ in etwas anderes als eine geordnete Zuordnung zu ändern, kann zu einem TypeError führen.
class C:
__annotations__ = 42
x: int = 5 # raises TypeError
(Beachten Sie, dass die Zuweisung an __annotations__, die schuldige ist, vom Python-Interpreter ohne Nachfrage akzeptiert wird – aber die nachfolgende Typ-Annotation erwartet, dass es sich um ein MutableMapping handelt und wird fehlschlagen.)
Der empfohlene Weg, Annotationen zur Laufzeit abzurufen, ist die Verwendung der Funktion typing.get_type_hints; wie bei allen Dunder-Attributen unterliegt jede undokumentierte Verwendung von __annotations__ dem Risiko von Bruch ohne Vorwarnung.
from typing import Dict, ClassVar, get_type_hints
class Starship:
hitpoints: int = 50
stats: ClassVar[Dict[str, int]] = {}
shield: int = 100
captain: str
def __init__(self, captain: str) -> None:
...
assert get_type_hints(Starship) == {'hitpoints': int,
'stats': ClassVar[Dict[str, int]],
'shield': int,
'captain': str}
assert get_type_hints(Starship.__init__) == {'captain': str,
'return': None}
Beachten Sie, dass, wenn Annotationen nicht statisch gefunden werden, das Wörterbuch __annotations__ gar nicht erstellt wird. Auch der Wert, Annotationen lokal verfügbar zu haben, gleicht nicht die Kosten für die Erstellung und Befüllung des Annotationswörterbuchs bei jedem Funktionsaufruf aus. Daher werden Annotationen auf Funktionsebene nicht ausgewertet und nicht gespeichert.
Andere Verwendungen von Annotationen
Während Python mit diesem PEP nicht dagegen haben wird:
alice: 'well done' = 'A+'
bob: 'what a shame' = 'F-'
da es die Typ-Annotation nicht weiter als "es wird ohne Fehler ausgewertet" kümmert, wird ein Typ-Checker, der darauf stößt, sie melden, es sei denn, er wird mit # type: ignore oder @no_type_check deaktiviert.
Wenn der obige Schnipsel jedoch auf globaler Ebene oder in einer Klasse steht, wird __annotations__ {'alice': 'gut gemacht', 'bob': 'was für eine Schande'} enthalten, da Python sich nicht um den "Typ" kümmert.
Diese gespeicherten Annotationen könnten für andere Zwecke verwendet werden, aber mit diesem PEP empfehlen wir explizit Typ-Hinting als bevorzugte Verwendung von Annotationen.
Abgelehnte/verschobene Vorschläge
- Sollen wir überhaupt Variablenannotationen einführen? Variablenannotationen sind bereits seit fast zwei Jahren in Form von Typ-Kommentaren vorhanden, die durch PEP 484 sanktioniert wurden. Sie werden von Drittanbieter-Typ-Checkern (mypy, pytype, PyCharm usw.) und von Projekten, die die Typ-Checker verwenden, intensiv genutzt. Die Kommentar-Syntax hat jedoch viele Nachteile, die im Abschnitt Rationale aufgeführt sind. Dieses PEP befasst sich nicht mit der Notwendigkeit von Typ-Annotationen, sondern mit der Syntax für solche Annotationen.
- Ein neues Schlüsselwort einführen: Die Wahl eines guten Schlüsselworts ist schwierig, z. B. kann es nicht
varsein, da dies ein zu häufiger Variablenname ist, und es kann nichtlocalsein, wenn wir es für Klassenvariablen oder globale Variablen verwenden wollen. Zweitens müssten wir, egal was wir wählen, immer noch ein__future__-Import durchführen. - Verwenden von
defals Schlüsselwort: Der Vorschlag wäredef primes: List[int] = [] def captain: str
Das Problem dabei ist, dass
deffür Generationen von Python-Programmierern (und Werkzeugen!) "definiere eine Funktion" bedeutet, und die Verwendung auch zur Definition von Variablen erhöht die Klarheit nicht. (Das ist natürlich subjektiv.) - Verwendung einer funktionsbasierten Syntax: Es wurde vorgeschlagen, die Typen von Variablen mit
var = cast(annotation[, value])zu annotieren. Obwohl diese Syntax einige Probleme mit Typ-Kommentaren wie die Abwesenheit der Annotation in AST löst, löst sie andere Probleme wie Lesbarkeit nicht und führt zu möglichen Laufzeit-Overheads. - Typ-Annotationen für Tupelentpackung zulassen: Dies führt zu Mehrdeutigkeiten: es ist nicht klar, was diese Anweisung bedeutet.
x, y: T
Sind
xundybeide vom TypT, oder erwarten wir, dassTein Tupeltyp von zwei Elementen ist, die aufxundyverteilt werden, oder vielleicht hatxden TypAnyundyhat den TypT? (Letzteres wäre die Bedeutung, wenn dies in einer Funktionssignatur auftreten würde.) Anstatt den (menschlichen) Leser raten zu lassen, verbieten wir dies, zumindest vorerst. - Klammerform
(var: type)für Annotationen: Dies wurde auf python-ideas als Abhilfe für die oben genannte Mehrdeutigkeit angesprochen, aber abgelehnt, da eine solche Syntax umständlich wäre, die Vorteile gering sind und die Lesbarkeit schlecht wäre. - Annotationen in verketteten Zuweisungen zulassen: Dies hat Probleme mit Mehrdeutigkeit und Lesbarkeit, die denen der Tupelentpackung ähneln, zum Beispiel in:
x: int = y = 1 z = w: int = 1
Es ist mehrdeutig, welche Typen von
yundzsein sollen? Auch die zweite Zeile ist schwer zu parsen. - Annotationen in
withundforAnweisungen zulassen: Dies wurde abgelehnt, da es beiforschwierig wäre, den eigentlichen Iterable zu erkennen, und beiwithden LL(1)-Parser von CPython verwirren würde. - Lokale Annotationen zum Zeitpunkt der Funktionsdefinition auswerten: Dies wurde von Guido abgelehnt, da die Platzierung der Annotation stark darauf hindeutet, dass sie sich im selben Gültigkeitsbereich wie der umgebende Code befindet.
- Variablenannotationen auch im Funktionsbereich speichern: Der Wert, Annotationen lokal verfügbar zu haben, reicht nicht aus, um die Kosten für die Erstellung und Befüllung des Wörterbuchs bei *jedem* Funktionsaufruf signifikant auszugleichen.
- Initialisierung von Variablen, die ohne Zuweisung annotiert sind: Es wurde auf python-ideas vorgeschlagen,
xinx: intmitNoneoder einem zusätzlichen speziellen Konstanten wie dem Javascript-Äquivalentundefinedzu initialisieren. Das Hinzufügen eines weiteren Singleton-Werts zur Sprache müsste jedoch überall im Code überprüft werden. Daher hat Guido dazu einfach "Nein" gesagt. - Auch
InstanceVarzum typing-Modul hinzufügen: Dies ist redundant, da Instanzvariablen weitaus häufiger vorkommen als Klassenvariablen. Die häufigere Verwendung verdient es, der Standard zu sein. - Nur Instanzvariablenannotationen in Methoden zulassen: Das Problem ist, dass viele
__init__-Methoden viele Dinge tun, die über die Initialisierung von Instanzvariablen hinausgehen, und es wäre schwieriger (für einen Menschen), alle Instanzvariablenannotationen zu finden. Und manchmal wird__init__in mehrere Hilfsmethoden aufgeteilt, so dass es noch schwieriger ist, sie aufzuspüren. Die Instanzvariablenannotationen zusammen in der Klasse zu platzieren, macht sie leichter auffindbar und hilft einem Erstleser des Codes. - Syntax
x: class t = vfür Klassenvariablen verwenden: Dies würde einen komplizierteren Parser erfordern und das Schlüsselwortclasswürde einfach gestrickte Syntaxhervorhebungen verwirren. Jedenfalls müssen wirClassVarverwenden, um Klassenvariablen in__annotations__zu speichern, daher wurde eine einfachere Syntax gewählt. ClassVarganz vergessen: Dies wurde vorgeschlagen, da mypy anscheinend gut ohne eine Möglichkeit auskommt, zwischen Klassen- und Instanzvariablen zu unterscheiden. Aber ein Typ-Checker kann mit den zusätzlichen Informationen nützliche Dinge tun, zum Beispiel versehentliche Zuweisungen zu einer Klassenvariable über die Instanz melden (was eine Instanzvariable erzeugen würde, die die Klassenvariable überschattet). Er könnte auch Instanzvariablen mit veränderlichen Standardwerten melden, eine bekannte Gefahr.ClassAttranstelle vonClassVarverwenden: Der Hauptgrund, warumClassVarbesser ist, ist folgender: viele Dinge sind Klassenattribute, z. B. Methoden, Deskriptoren usw. Aber nur bestimmte Attribute sind konzeptionell Klassenvariablen (oder vielleicht Konstanten).- Annotationen nicht auswerten, sie als Zeichenketten behandeln: Dies wäre inkonsistent mit dem Verhalten von Funktionsannotationen, die immer ausgewertet werden. Obwohl dies in Zukunft überdacht werden könnte, wurde in PEP 484 entschieden, dass dies ein separates PEP sein müsste.
- Variablentypen in der Klassendokumentation annotieren: Viele Projekte verwenden bereits verschiedene Docstring-Konventionen, oft ohne viel Konsistenz und generell ohne die PEP 484 Annotationssyntax bereits zu erfüllen. Auch dies würde einen speziellen hochentwickelten Parser erfordern. Dies würde wiederum den Zweck des PEPs untergraben – die Zusammenarbeit mit Drittanbieter-Typ-Checking-Tools.
__annotations__als Deskriptor implementieren: Dies wurde vorgeschlagen, um zu verhindern, dass__annotations__auf etwas Nicht-Wörterbuch- oder Nicht-None-artiges gesetzt wird. Guido hat diese Idee als unnötig abgelehnt; stattdessen wird ein TypeError ausgelöst, wenn versucht wird,__annotations__zu aktualisieren, wenn es sich um etwas anderes als eine Zuordnung handelt.- Nackte Annotationen wie global oder nonlocal behandeln: Der abgelehnte Vorschlag würde bevorzugen, dass das Vorhandensein einer Annotation ohne Zuweisung im Funktionskörper *keine* Auswertung beinhaltet. Im Gegensatz dazu impliziert das PEP, dass, wenn das Ziel komplexer als ein einzelner Name ist, sein "linkes Teil" an der Stelle, an der es im Funktionskörper vorkommt, ausgewertet werden sollte, nur um sicherzustellen, dass es definiert ist. Zum Beispiel in diesem Beispiel:
def foo(self): slef.name: str
Der Name
slefsollte ausgewertet werden, nur damit, wenn er nicht definiert ist (wie in diesem Beispiel wahrscheinlich :-)), der Fehler zur Laufzeit abgefangen wird. Dies entspricht eher dem, was passiert, wenn ein Anfangswert vorhanden ist, und führt daher zu weniger Überraschungen. (Beachten Sie auch, dass, wenn das Zielself.namewäre (diesmal richtig geschrieben :-)), ein optimierender Compiler keine Verpflichtung hat,selfauszuwerten, solange er beweisen kann, dass es definitiv definiert sein wird.)
Abwärtskompatibilität
Dieser PEP ist vollständig abwärtskompatibel.
Implementierung
Eine Implementierung für Python 3.6 ist auf GitHub zu finden.
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0526.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT