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

Python Enhancement Proposals

PEP 613 – Explizite Typ-Aliase

Autor:
Shannon Zhu <szhu at fb.com>
Sponsor:
Guido van Rossum <guido at python.org>
Discussions-To:
Typing-SIG-Thread
Status:
Final
Typ:
Standards Track
Thema:
Typisierung
Erstellt:
21. Januar 2020
Python-Version:
3.10
Post-History:
21. Januar 2020

Inhaltsverzeichnis

Wichtig

Dieses PEP ist ein historisches Dokument: siehe Typ-Aliase und typing.TypeAlias für aktuelle Spezifikationen und Dokumentation. Kanonische Typ-Spezifikationen werden auf der Typ-Spezifikationsseite gepflegt; das Laufzeitverhalten für Typen wird in der CPython-Dokumentation beschrieben.

×

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

Zusammenfassung

Typ-Aliase sind vom Benutzer spezifizierte Typen, die so komplex sein können wie jede Typ-Annotation und mit einer einfachen Variablenausweisung auf Modulebene spezifiziert werden.

Dieses PEP formalisiert eine Methode zur expliziten Deklaration einer Zuweisung als Typ-Alias.

Motivation

Typ-Aliase werden als Variablenausweisungen auf oberster Ebene deklariert. In PEP 484 wurde die Unterscheidung zwischen einem gültigen Typ-Alias und einer globalen Variable implizit bestimmt: Wenn eine Zuweisung auf oberster Ebene nicht annotiert ist und der zugewiesene Wert ein gültiger Typ ist, dann ist der Name, dem zugewiesen wird, ein gültiger Typ-Alias. Andernfalls ist dieser Name einfach ein globaler Wert, der nicht als Typ-Annotation verwendet werden kann.

Diese impliziten Regeln zur Deklaration von Typ-Aliasen führen zu Verwirrung, wenn Typ-Aliase Vorwärtsreferenzen, ungültige Typen enthalten oder andere Einschränkungen verletzen, die für die Deklaration von Typ-Aliasen gelten. Da die Unterscheidung zwischen einem nicht annotierten Wert und einem Typ-Alias implizit ist, werden mehrdeutige oder fehlerhafte Deklarationen von Typ-Aliasen implizit als gültige Wertzuweisung behandelt. Dies führt zu Ausdrücken, die sich nicht als Typ-Aliase ausdrücken lassen, und verschiebt die Fehlerdiagnose von fehlerhaften Typ-Aliasen nach unten.

Die folgenden Beispiele enthalten jeweils eine Illustration einiger suboptimalen oder verwirrenden Verhaltensweisen, die sich aus bestehenden impliziten Alias-Deklarationen ergeben. Zum Vergleich führen wir hier auch explizite Aliase im Format TypName: TypeAlias = Ausdruck ein, aber die Syntax wird in späteren Abschnitten detaillierter diskutiert.

Vorwärtsreferenzen

MyType = "ClassName"
def foo() -> MyType: ...

Dieser Code-Schnipsel sollte keinen Fehler verursachen, solange ClassName später definiert wird. Ein Typ-Checker ist jedoch gezwungen anzunehmen, dass MyType eine Wertzuweisung und kein Typ-Alias ist, und kann daher fälschlicherweise Fehler ausgeben, dass (1) MyType ein nicht annotierter globaler String ist und (2) MyType nicht als Rückgabeannotation verwendet werden kann, da es kein gültiger Typ ist.

MyType: TypeAlias = "ClassName"
def foo() -> MyType: ...

Explizite Aliase beseitigen Mehrdeutigkeit, sodass keiner der oben genannten Fehler ausgelöst wird. Wenn außerdem etwas mit ClassName nicht stimmt (d.h. es ist tatsächlich später nicht definiert), kann der Typ-Checker einen Fehler ausgeben.

Fehlermeldungen

MyType1 = InvalidType
MyType2 = MyGeneric(int)  # i.e., intention was MyGeneric[int]

Ein Typ-Checker sollte diesen Code-Schnipsel warnen, dass InvalidType kein gültiger Typ ist und daher nicht zur Annotation eines Ausdrucks oder zur Konstruktion eines Typ-Alias verwendet werden kann. Stattdessen sind Typ-Checker gezwungen, fälschlicherweise Fehler auszugeben, dass (1) MyType ein globaler Ausdruck ohne Annotation ist und (2) MyType in allen Verwendungen von MyType im Codebestand kein gültiger Typ ist.

MyType1: TypeAlias = InvalidType
MyType2: TypeAlias = MyGeneric(int)

Mit expliziten Aliasen verfügt der Typ-Checker über genügend Informationen, um bei der tatsächlichen Definition des fehlerhaften Typ-Alias einen Fehler auszugeben und zu erklären, warum: dass MyGeneric(int) und InvalidType keine gültigen Typen sind. Wenn der Ausdruckswert nicht mehr als globaler Wert ausgewertet wird, können nicht umsetzbare Typfehler bei allen Verwendungen von MyType im gesamten Codebestand unterdrückt werden.

Geltungsbereichsbeschränkungen

class Foo:
  x = ClassName
  y: TypeAlias = ClassName
  z: Type[ClassName] = ClassName

Typ-Aliase sind im Klassengeltungsbereich gültig, sowohl implizit (x) als auch explizit (y). Wenn die Zeile als Klassenvariable interpretiert werden soll, muss sie explizit annotiert werden (z).

x = ClassName
def foo() -> None:
  x = ClassName

Das äußere x ist ein gültiger Typ-Alias, aber Typ-Checker müssen einen Fehler ausgeben, wenn das innere x jemals als Typ verwendet wird, da Typ-Aliase nicht innerhalb einer Funktion definiert werden können. Dies ist verwirrend, da die Regel für die Alias-Deklaration nicht explizit ist und da kein Typfehler an der Stelle der inneren Typ-Alias-Deklaration, sondern an jedem ihrer nachfolgenden Verwendungsfälle ausgelöst wird.

x: TypeAlias = ClassName
def foo() -> None:
  x = ClassName
def bar() -> None:
  x: TypeAlias = ClassName

Mit expliziten Aliasen ist die äußere Zuweisung immer noch eine gültige Typvariable. Innerhalb von foo sollte die innere Zuweisung als x: Type[ClassName] interpretiert werden. Innerhalb von bar sollte der Typ-Checker einen klaren Fehler ausgeben, der dem Autor mitteilt, dass Typ-Aliase nicht innerhalb einer Funktion definiert werden können.

Spezifikation

Die explizite Syntax für die Alias-Deklaration unterscheidet klar zwischen den drei möglichen Arten von Zuweisungen: typisierte globale Ausdrücke, nicht typisierte globale Ausdrücke und Typ-Aliase. Dies vermeidet Zuweisungen, die die Typüberprüfung beim Hinzufügen einer Annotation brechen, und vermeidet die Klassifizierung der Art der Zuweisung basierend auf dem Typ des Werts.

Implizite Syntax (vorhanden)

x = 1  # untyped global expression
x: int = 1  # typed global expression

x = int  # type alias
x: Type[int] = int  # typed global expression

Explizite Syntax

x = 1  # untyped global expression
x: int = 1  # typed global expression

x = int  # untyped global expression (see note below)
x: Type[int] = int  # typed global expression

x: TypeAlias = int  # type alias
x: TypeAlias = "MyClass"  # type alias

Hinweis: Die obigen Beispiele veranschaulichen implizite und explizite Alias-Deklarationen isoliert. Aus Gründen der Abwärtskompatibilität sollten Typ-Checker beide gleichzeitig unterstützen, was bedeutet, dass ein nicht typisierter globaler Ausdruck x = int immer noch als gültiger Typ-Alias betrachtet wird.

Abwärtskompatibilität

Explizite Aliase bieten eine alternative Möglichkeit, Typ-Aliase zu deklarieren, aber der gesamte bestehende Code und alte Alias-Deklarationen funktionieren weiterhin wie bisher.

Referenzimplementierung

Der Pyre Typ-Checker unterstützt explizite Typ-Alias-Deklarationen.

Abgelehnte Ideen

Einige alternative Syntaxen wurden für explizite Aliase in Betracht gezogen

MyType: TypeAlias[int]

Dies sieht sehr nach einer nicht initialisierten Variable aus.

MyType = TypeAlias[int]

Neben der obigen Option birgt dieses Format die Gefahr, Verwirrung darüber zu stiften, was der Laufzeitwert von MyType ist.

Im Vergleich dazu ist die gewählte Syntaxoption MyType: TypeAlias = int ansprechend, da sie weiterhin die Zuweisungssyntax MyType = int beibehält und lediglich um Informationen für den Typ-Checker als Annotation ergänzt.

Versionshistorie

  • 2021-11-16
    • Erlaube TypeAlias im Klassengeltungsbereich

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

Zuletzt geändert: 2024-06-11 22:12:09 GMT