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

Python Enhancement Proposals

PEP 604 – Zulässigkeit der Schreibweise von Union-Typen als X | Y

Autor:
Philippe PRADOS <python at prados.fr>, Maggie Moss <maggiebmoss at gmail.com>
Sponsor:
Chris Angelico <rosuav at gmail.com>
BDFL-Delegate:
Guido van Rossum <guido at python.org>
Discussions-To:
Typing-SIG list
Status:
Final
Typ:
Standards Track
Thema:
Typisierung
Erstellt:
28-Aug-2019
Python-Version:
3.10
Post-History:
28-Aug-2019, 05-Aug-2020

Inhaltsverzeichnis

Wichtig

Diese PEP ist ein historisches Dokument. Die aktuelle, kanonische Dokumentation finden Sie unter Union Type.

×

Siehe PEP 1, um Änderungen vorzuschlagen.

Zusammenfassung

Diese PEP schlägt vor, den Operator | für Typen zu überladen, um Union[X, Y] als X | Y zu schreiben, und erlaubt dessen Verwendung in isinstance- und issubclass-Aufrufen.

Motivation

PEP 484 und PEP 526 schlagen eine generische Syntax für die Typisierung von Variablen, Parametern und Funktionsrückgaben vor. PEP 585 schlägt vor, Parameter für Generics zur Laufzeit verfügbar zu machen. Mypy [1] akzeptiert eine Syntax, die aussieht wie

annotation: name_type
name_type: NAME (args)?
args: '[' paramslist ']'
paramslist: annotation (',' annotation)* [',']
  • Um eine Disjunktion (Union-Typ) zu beschreiben, muss der Benutzer Union[X, Y] verwenden.

Die Ausführlichkeit dieser Syntax trägt nicht zur Akzeptanz von Typen bei.

Vorschlag

Inspiriert von Scala [2] und Pike [3], fügt dieser Vorschlag den Operator type.__or__() hinzu. Mit diesem neuen Operator ist es möglich, int | str anstelle von Union[int, str] zu schreiben. Zusätzlich zu Annotationen wäre das Ergebnis dieses Ausdrucks dann in isinstance() und issubclass() gültig.

isinstance(5, int | str)
issubclass(bool, int | float)

Wir werden auch in der Lage sein, t | None oder None | t anstelle von Optional[t] zu schreiben.

isinstance(None, int | None)
isinstance(42, None | int)

Spezifikation

Die neue Union-Syntax sollte für Funktions-, Variablen- und Parameterannotationen akzeptiert werden.

Vereinfachte Syntax

# Instead of
# def f(list: List[Union[int, str]], param: Optional[int]) -> Union[float, str]
def f(list: List[int | str], param: int | None) -> float | str:
    pass

f([1, "abc"], None)

# Instead of typing.List[typing.Union[str, int]]
typing.List[str | int]
list[str | int]

# Instead of typing.Dict[str, typing.Union[int, float]]
typing.Dict[str, int | float]
dict[str, int | float]

Das bestehende typing.Union und die |-Syntax sollten äquivalent sein.

int | str == typing.Union[int, str]

typing.Union[int, int] == int
int | int == int

Die Reihenfolge der Elemente in der Union sollte für die Gleichheit keine Rolle spielen.

(int | str) == (str | int)
(int | str | float) == typing.Union[str, float, int]

Optionale Werte sollten äquivalent zur neuen Union-Syntax sein.

None | t == typing.Optional[t]

Eine neue `Union.__repr__()`-Methode sollte implementiert werden.

str(int | list[str])
# int | list[str]

str(int | int)
# int

isinstance und issubclass

Die neue Syntax sollte für Aufrufe von isinstance und issubclass akzeptiert werden, solange die Union-Elemente selbst gültige Argumente für isinstance und issubclass sind.

# valid
isinstance("", int | str)

# invalid
isinstance(2, list[int]) # TypeError: isinstance() argument 2 cannot be a parameterized generic
isinstance(1, int | list[int])

# valid
issubclass(bool, int | float)

# invalid
issubclass(bool, bool | list[int])

Inkompatible Änderungen

In einigen Situationen werden einige Ausnahmen nicht wie erwartet ausgelöst.

Wenn eine Metaklasse den Operator __or__ implementiert, wird sie diesen überschreiben.

>>> class M(type):
...     def __or__(self, other): return "Hello"
...
>>> class C(metaclass=M): pass
...
>>> C | int
'Hello'
>>> int | C
typing.Union[int, __main__.C]
>>> Union[C, int]
typing.Union[__main__.C, int]

Einwände und Antworten

Weitere Details zu den Diskussionen finden Sie unten.

1. Einen neuen Operator für Union[type1, type2] hinzufügen?

Vorteile

  • Diese Syntax kann lesbarer sein und ähnelt anderen Sprachen (Scala, …).
  • Zur Laufzeit könnte int|str in 3.10 ein einfaches Objekt zurückgeben, anstatt alles, was man aus dem Import von typing holen müsste.

Nachteile

  • Das Hinzufügen dieses Operators führt zu einer Abhängigkeit zwischen typing und builtins.
  • Bricht den Backport (da typing leicht zurückportiert werden kann, aber die Kern-types nicht).
  • Wenn Python selbst nicht geändert werden muss, müssten wir es immer noch in mypy, Pyre, PyCharm, Pytype und wer weiß was noch implementieren (es ist eine geringfügige Änderung, siehe „Referenzimplementierung“).

2. Nur PEP 484 (Type Hints) ändern, um die Syntax type1 | type2 zu akzeptieren?

PEP 563 (Postponed Evaluation of Annotations) reicht aus, um diesen Vorschlag zu akzeptieren, wenn wir uns darauf einigen, nicht mit der dynamischen Auswertung von Annotationen (eval()) kompatibel zu sein.

>>> from __future__ import annotations
>>> def foo() -> int | str: pass
...
>>> eval(foo.__annotations__['return'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
TypeError: unsupported operand type(s) for |: 'type' and 'type'

3. isinstance() und issubclass() erweitern, um Union zu akzeptieren?

isinstance(x, str | int) ==> "is x an instance of str or int"

Vorteile

  • Wenn sie erlaubt wären, könnte die Instanzprüfung eine sehr übersichtliche Notation verwenden.

Nachteile

  • Das gesamte typing-Modul muss in builtin migriert werden.

Referenzimplementierung

Ein neuer integrierter Union-Typ muss implementiert werden, um den Rückgabewert von t1 | t2 zu speichern, und er muss von isinstance() und issubclass() unterstützt werden. Dieser Typ kann im types-Modul platziert werden. Die Interoperabilität zwischen types.Union und typing.Union muss gewährleistet sein.

Sobald die Python-Sprache erweitert ist, müssen mypy [1] und andere Typ-Checker aktualisiert werden, um diese neue Syntax zu akzeptieren.

Referenzen


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

Zuletzt geändert: 2024-02-16 17:06:07 GMT