PEP 663 – Standardisierung des Enum str(), repr(), und format() Verhaltens
- Autor:
- Ethan Furman <ethan at stoneleaf.us>
- Discussions-To:
- Python-Dev Liste
- Status:
- Abgelehnt
- Typ:
- Informational
- Erstellt:
- 30. Juni 2021
- Python-Version:
- 3.11
- Post-History:
- 20. Juli 2021, 02. Nov. 2021
- Resolution:
- Python-Dev Nachricht
Inhaltsverzeichnis
Zusammenfassung
Aktualisieren Sie repr(), str() und format() der verschiedenen Enum-Typen, um sie besser an ihren beabsichtigten Zweck anzupassen. Zum Beispiel wird str() von IntEnum geändert, um seinem format() zu entsprechen, während ein benutzerdefiniertes gemischtes Int-Enum sein format() an sein str() anpasst. In allen Fällen sind das str() und format() eines Enums identisch (es sei denn, der Benutzer überschreibt format()).
Fügen Sie einen globalen Enum-Decorator hinzu, der das str() und repr() (und format()) des dekorierten Enums so ändert, dass es sich um einen gültigen globalen Verweis handelt: d. h. re.IGNORECASE anstelle von <RegexFlag.IGNORECASE: 2>.
Motivation
Wenn str() von IntEnum und IntFlag nicht der Wert ist, verursacht dies Fehler und zusätzlichen Aufwand beim Ersetzen bestehender Konstanten.
Wenn str() und format() eines Enum-Mitglieds unterschiedlich sind, kann dies verwirrend sein.
Die Hinzufügung von StrEnum mit der Anforderung, dass sein str() sein value ist, ist inkonsistent mit dem str anderer bereitgestellter Enums.
Die Iteration von Flag-Mitgliedern, die sich direkt auf ihr repr() auswirkt, ist bestenfalls ungeschickt und schlimmstenfalls fehlerhaft.
Begründung
Enums werden in der Standardbibliothek immer häufiger verwendet; die Möglichkeit, Enum-Mitglieder anhand ihres repr() zu erkennen, und dieses repr() leicht parsen zu können, ist nützlich und kann Zeit und Mühe beim Verstehen und Debuggen von Code sparen.
Die Enums mit gemischten Datentypen (IntEnum, IntFlag und das neue StrEnum) müssen jedoch rückwärtskompatibler mit den Konstanten sein, die sie ersetzen – insbesondere sollte str(replacement_enum_member) == str(original_constant) wahr sein (und dasselbe gilt für format()).
IntEnum, IntFlag und StrEnum sollten so nah wie möglich eine Drop-in-Ersetzung für bestehende Integer- und String-Konstanten sein. Zu diesem Zweck sollte die str()-Ausgabe jeder das inhärente Element sein; z. B. wenn Color ein IntEnum ist
>>> Color.RED
<Color.RED: 1>
>>> str(Color.RED)
'1'
>>> format(Color.RED)
'1'
Beachten Sie, dass format() bereits die korrekte Ausgabe liefert, nur str() muss aktualisiert werden.
Soweit wie möglich sollten str(), repr() und format() von Enum-Mitgliedern über die Standardbibliothek hinweg standardisiert werden. Bis Python 3.10 haben jedoch mehrere Enums in der Standardbibliothek eine benutzerdefinierte str() und/oder repr().
Das repr() von Flag enthält derzeit Aliase, was es nicht sollte; die Korrektur dessen ändert sein repr() in bestimmten Fällen bereits.
Spezifikation
Es gibt drei Hauptkategorien der Enum-Nutzung
- einfach:
EnumoderFlageine neue Enum-Klasse wird ohne Datenmixins erstellt - Drop-in-Ersatz:
IntEnum,IntFlag,StrEnumeine neue Enum-Klasse wird erstellt, die auch vonintoderstrerbt undint.__str__oderstr.__str__verwendet - benutzerdefinierte gemischte Enums und Flags der Benutzer erstellt seine eigenen Integer-, Float-, String- usw.-Enums anstelle der Verwendung von enum.IntEnum usw.
Es gibt auch zwei Stile
- normal: Die Enumerationsmitglieder verbleiben in ihren Klassen und werden als
klassenname.mitgliednameangesprochen, und der Klassenname erscheint in ihremrepr()undstr()(wo angebracht) - global: Die Enumerationsmitglieder werden in den globalen Namensraum ihres Moduls kopiert, und der Modulname erscheint in ihrem
repr()undstr()(wo angebracht)
Einige Beispiel-Enums
# module: tools.py
class Hue(Enum): # or IntEnum
LIGHT = -1
NORMAL = 0
DARK = +1
class Color(Flag): # or IntFlag
RED = 1
GREEN = 2
BLUE = 4
class Grey(int, Enum): # or (int, Flag)
BLACK = 0
WHITE = 1
Unter Verwendung der obigen Aufzählungen zeigen die folgenden beiden Tabellen die alte und die neue Ausgabe (leere Zellen bedeuten keine Änderung)
| Stil | Kategorie | enum repr() | enum str() | enum format() | |
| normal | einfach | 3.10 | |||
| neu | |||||
| benutzerdefiniert gemischt | 3.10 | 1 | |||
| neu | Grau.WEISS | ||||
| int drop-in | 3.10 | Farbe.HELL | |||
| neu | -1 | ||||
| global | einfach | 3.10 | <Farbe.HELL: -1> | Farbe.HELL | Farbe.HELL |
| neu | werkzeuge.HELL | HELL | HELL | ||
| benutzerdefiniert gemischt | 3.10 | <Grau.WEISS: 1 | Grau.WEISS | Grau.WEISS | |
| neu | werkzeuge.WEISS | WEISS | WEISS | ||
| int drop-in | 3.10 | <Farbe.HELL: -1> | Farbe.HELL | ||
| neu | werkzeuge.HELL | -1 | |||
| Stil | Kategorie | flag repr() | flag str() | flag format() | |
| normal | einfach | 3.10 | <Farbe.ROT|GRÜN: 3> | Farbe.ROT|GRÜN | Farbe.ROT|GRÜN |
| neu | <Farbe(3): ROT|GRÜN> | Farbe.ROT|Farbe.GRÜN | Farbe.ROT|Farbe.GRÜN | ||
| benutzerdefiniert gemischt | 3.10 | <Grau.WEISS: 1> | 1 | ||
| neu | <Grau(1): WEISS> | Grau.WEISS | |||
| int drop-in | 3.10 | <Farbe.ROT|GRÜN: 3> | Farbe.ROT|GRÜN | ||
| neu | <Farbe(3): ROT|GRÜN> | 3 | |||
| global | einfach | 3.10 | <Farbe.ROT|GRÜN: 3> | Farbe.ROT|GRÜN | Farbe.ROT|GRÜN |
| neu | werkzeuge.ROT|werkzeuge.GRÜN | ROT|GRÜN | ROT|GRÜN | ||
| benutzerdefiniert gemischt | 3.10 | <Grau.WEISS: 1> | Grau.WEISS | 1 | |
| neu | werkzeuge.WEISS | WEISS | WEISS | ||
| int drop-in | 3.10 | <Farbe.ROT|GRÜN: 3> | Farbe.ROT|GRÜN | ||
| neu | werkzeuge.ROT|werkzeuge.GRÜN | 3 | |||
Diese beiden Tabellen zeigen das Endergebnis
| Stil | Kategorie | enum repr() | enum str() | enum format() |
| normal | einfach | <Farbe.HELL: -1> | Farbe.HELL | Farbe.HELL |
| benutzerdefiniert gemischt | <Grau.WEISS: 1> | Grau.WEISS | Grau.WEISS | |
| int drop-in | <Farbe.HELL: -1> | -1 | -1 | |
| global | einfach | werkzeuge.HELL | HELL | HELL |
| benutzerdefiniert gemischt | werkzeuge.WEISS | WEISS | WEISS | |
| int drop-in | werkzeuge.HELL | -1 | -1 |
| Stil | Kategorie | flag repr() | flag str() | flag format() |
| normal | einfach | <Farbe(3): ROT|GRÜN> | Farbe.ROT|Farbe.GRÜN | Farbe.ROT|Farbe.GRÜN |
| benutzerdefiniert gemischt | <Grau(1): WEISS> | Grau.WEISS | Grau.WEISS | |
| int drop-in | <Farbe(3): ROT|GRÜN> | 3 | 3 | |
| global | einfach | werkzeuge.ROT|werkzeuge.GRÜN | ROT|GRÜN | ROT|GRÜN |
| benutzerdefiniert gemischt | werkzeuge.WEISS | WEISS | WEISS | |
| int drop-in | werkzeuge.ROT|werkzeuge.GRÜN | 3 | 3 |
Wie zu sehen ist, wird repr() hauptsächlich davon beeinflusst, ob die Elemente global sind, während str() davon beeinflusst wird, ob sie global sind oder ein Drop-in-Ersatz sind, wobei der Drop-in-Ersatz-Status Vorrang hat. Außerdem haben sich das grundlegende repr() und str() für Flags geändert, da der alte Stil fehlerhaft war.
Abwärtskompatibilität
Die Abwärtskompatibilität von stringifizierten Objekten ist über größere Python-Versionen hinweg nicht garantiert, und es wird zu Rückwärtskompatibilitätsproblemen kommen, wenn Software die repr(), str() und format() Ausgaben von Enums in Tests, Dokumentationen, Datenstrukturen und/oder Code-Generierung verwendet.
Die normale Verwendung von Enum-Mitgliedern wird sich nicht ändern: re.ASCII kann immer noch als re.ASCII verwendet werden und wird immer noch mit 256 gleich sein.
Wenn die vorherige Ausgabe beibehalten werden muss, um beispielsweise die Kompatibilität zwischen verschiedenen Python-Versionen sicherzustellen, müssen Softwareprojekte ihre eigene Enum-Basisklasse mit den entsprechenden überschriebenen Methoden erstellen.
Beachten Sie, dass wir durch die Änderung des str() der Drop-in-Kategorie zukünftige Fehler verhindern werden, wenn IntEnum usw. verwendet werden, um bestehende Konstanten zu ersetzen.
Urheberrecht
Dieses Dokument wird in die Public Domain oder unter die CC0-1.0-Universal-Lizenz gestellt, je nachdem, welche Lizenz permissiver ist.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0663.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT