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

Python Enhancement Proposals

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

Inhaltsverzeichnis

Wichtig

Dieses PEP ist ein historisches Dokument: siehe Annotated assignment statements, ClassVar und typing.ClassVar für aktuelle Spezifikationen und Dokumentation. Kanonische Tippspezifikationen werden auf der Website für Tippspezifikationen gepflegt (typing specs site); das Laufzeit-Tippverhalten wird in der CPython-Dokumentation beschrieben.

×

Siehe den Prozess zur Aktualisierung der Typ-Spezifikation, um Änderungen an der Typ-Spezifikation vorzuschlagen.

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 None initialisieren (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 Modul typing hinzugefü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 von ClassVar und den Abschnitt rejected für weitere Informationen über die Gründe für ClassVar.
  • Die Funktion get_type_hints im Modul typing wird 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 (vielleicht collections.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 var sein, da dies ein zu häufiger Variablenname ist, und es kann nicht local sein, 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 def als Schlüsselwort: Der Vorschlag wäre
    def primes: List[int] = []
    def captain: str
    

    Das Problem dabei ist, dass def fü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 x und y beide vom Typ T, oder erwarten wir, dass T ein Tupeltyp von zwei Elementen ist, die auf x und y verteilt werden, oder vielleicht hat x den Typ Any und y hat den Typ T? (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 y und z sein sollen? Auch die zweite Zeile ist schwer zu parsen.

  • Annotationen in with und for Anweisungen zulassen: Dies wurde abgelehnt, da es bei for schwierig wäre, den eigentlichen Iterable zu erkennen, und bei with den 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, x in x: int mit None oder einem zusätzlichen speziellen Konstanten wie dem Javascript-Äquivalent undefined zu 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 InstanceVar zum 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 = v für Klassenvariablen verwenden: Dies würde einen komplizierteren Parser erfordern und das Schlüsselwort class würde einfach gestrickte Syntaxhervorhebungen verwirren. Jedenfalls müssen wir ClassVar verwenden, um Klassenvariablen in __annotations__ zu speichern, daher wurde eine einfachere Syntax gewählt.
  • ClassVar ganz 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.
  • ClassAttr anstelle von ClassVar verwenden: Der Hauptgrund, warum ClassVar besser 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 slef sollte 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 Ziel self.name wäre (diesmal richtig geschrieben :-)), ein optimierender Compiler keine Verpflichtung hat, self auszuwerten, 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.


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

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