Enum HOWTO

Eine Enum ist eine Menge von symbolischen Namen, die eindeutigen Werten zugeordnet sind. Sie sind ähnlich wie globale Variablen, bieten jedoch eine nützlichere repr(), Gruppierung, Typsicherheit und einige andere Funktionen.

Sie sind am nützlichsten, wenn Sie eine Variable haben, die einen von einer begrenzten Auswahl von Werten annehmen kann. Zum Beispiel die Wochentage

>>> from enum import Enum
>>> class Weekday(Enum):
...     MONDAY = 1
...     TUESDAY = 2
...     WEDNESDAY = 3
...     THURSDAY = 4
...     FRIDAY = 5
...     SATURDAY = 6
...     SUNDAY = 7

Oder vielleicht die RGB-Primärfarben

>>> from enum import Enum
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3

Wie Sie sehen können, ist das Erstellen einer Enum so einfach wie das Schreiben einer Klasse, die selbst von Enum erbt.

Hinweis

Groß-/Kleinschreibung von Enum-Mitgliedern

Da Enums zur Darstellung von Konstanten verwendet werden und um Probleme mit Namenskonflikten zwischen Mixin-Klassenmethoden/-Attributen und Enum-Namen zu vermeiden, empfehlen wir dringend die Verwendung von GROSSBUCHSTABEN für Mitgliedernamen und werden diesen Stil in unseren Beispielen verwenden.

Abhängig von der Art des Enums ist der Wert eines Mitglieds möglicherweise wichtig oder auch nicht, aber auf jeden Fall kann dieser Wert verwendet werden, um das entsprechende Mitglied abzurufen

>>> Weekday(3)
<Weekday.WEDNESDAY: 3>

Wie Sie sehen können, zeigt die repr() eines Mitglieds den Enum-Namen, den Mitgliedernamen und den Wert an. Die str() eines Mitglieds zeigt nur den Enum-Namen und den Mitgliedernamen an.

>>> print(Weekday.THURSDAY)
Weekday.THURSDAY

Der Typ eines Enumerationsmitglieds ist das Enum, zu dem es gehört.

>>> type(Weekday.MONDAY)
<enum 'Weekday'>
>>> isinstance(Weekday.FRIDAY, Weekday)
True

Enum-Mitglieder haben ein Attribut, das nur ihren name enthält.

>>> print(Weekday.TUESDAY.name)
TUESDAY

Ebenso haben sie ein Attribut für ihren value.

>>> Weekday.WEDNESDAY.value
3

Im Gegensatz zu vielen Sprachen, die Enumerationen ausschließlich als Namens-/Wertpaare behandeln, können Python Enums Verhalten hinzugefügt werden. Beispielsweise hat datetime.date zwei Methoden, um den Wochentag zurückzugeben: weekday() und isoweekday(). Der Unterschied besteht darin, dass eine von ihnen von 0-6 zählt und die andere von 1-7. Anstatt dies selbst zu verfolgen, können wir eine Methode zum Weekday-Enum hinzufügen, um den Tag aus der date-Instanz zu extrahieren und das passende Enum-Mitglied zurückzugeben.

@classmethod
def from_date(cls, date):
    return cls(date.isoweekday())

Das vollständige Weekday-Enum sieht nun so aus:

>>> class Weekday(Enum):
...     MONDAY = 1
...     TUESDAY = 2
...     WEDNESDAY = 3
...     THURSDAY = 4
...     FRIDAY = 5
...     SATURDAY = 6
...     SUNDAY = 7
...     #
...     @classmethod
...     def from_date(cls, date):
...         return cls(date.isoweekday())

Jetzt können wir herausfinden, welcher Tag heute ist! Beachten Sie:

>>> from datetime import date
>>> Weekday.from_date(date.today())
<Weekday.TUESDAY: 2>

Natürlich sehen Sie, wenn Sie dies an einem anderen Tag lesen, diesen Tag stattdessen.

Dieses Weekday-Enum ist großartig, wenn unsere Variable nur einen Tag benötigt, aber was ist, wenn wir mehrere benötigen? Vielleicht schreiben wir eine Funktion zum Plotten von Aufgaben während einer Woche und möchten keine Liste verwenden – wir könnten einen anderen Typ von Enum verwenden.

>>> from enum import Flag
>>> class Weekday(Flag):
...     MONDAY = 1
...     TUESDAY = 2
...     WEDNESDAY = 4
...     THURSDAY = 8
...     FRIDAY = 16
...     SATURDAY = 32
...     SUNDAY = 64

Wir haben zwei Dinge geändert: Wir erben von Flag, und die Werte sind alles Potenzen von 2.

Genau wie das ursprüngliche Weekday-Enum oben, können wir eine einzelne Auswahl haben.

>>> first_week_day = Weekday.MONDAY
>>> first_week_day
<Weekday.MONDAY: 1>

Aber Flag erlaubt es uns auch, mehrere Mitglieder zu einer einzigen Variablen zu kombinieren.

>>> weekend = Weekday.SATURDAY | Weekday.SUNDAY
>>> weekend
<Weekday.SATURDAY|SUNDAY: 96>

Sie können sogar über eine Flag-Variable iterieren.

>>> for day in weekend:
...     print(day)
Weekday.SATURDAY
Weekday.SUNDAY

Okay, richten wir ein paar Aufgaben ein.

>>> chores_for_ethan = {
...     'feed the cat': Weekday.MONDAY | Weekday.WEDNESDAY | Weekday.FRIDAY,
...     'do the dishes': Weekday.TUESDAY | Weekday.THURSDAY,
...     'answer SO questions': Weekday.SATURDAY,
...     }

Und eine Funktion zur Anzeige der Aufgaben für einen bestimmten Tag.

>>> def show_chores(chores, day):
...     for chore, days in chores.items():
...         if day in days:
...             print(chore)
...
>>> show_chores(chores_for_ethan, Weekday.SATURDAY)
answer SO questions

In Fällen, in denen die tatsächlichen Werte der Mitglieder keine Rolle spielen, können Sie sich etwas Arbeit sparen und auto() für die Werte verwenden.

>>> from enum import auto
>>> class Weekday(Flag):
...     MONDAY = auto()
...     TUESDAY = auto()
...     WEDNESDAY = auto()
...     THURSDAY = auto()
...     FRIDAY = auto()
...     SATURDAY = auto()
...     SUNDAY = auto()
...     WEEKEND = SATURDAY | SUNDAY

Programmatischer Zugriff auf Enumerationsmitglieder und ihre Attribute

Manchmal ist es nützlich, auf Mitglieder in Enumerationen programmatisch zuzugreifen (d. h. Situationen, in denen Color.RED nicht ausreicht, da die genaue Farbe zur Programm-Schreibzeit nicht bekannt ist). Enum ermöglicht einen solchen Zugriff.

>>> Color(1)
<Color.RED: 1>
>>> Color(3)
<Color.BLUE: 3>

Wenn Sie per Name auf Enum-Mitglieder zugreifen möchten, verwenden Sie den Elementzugriff.

>>> Color['RED']
<Color.RED: 1>
>>> Color['GREEN']
<Color.GREEN: 2>

Wenn Sie ein Enum-Mitglied haben und dessen name oder value benötigen.

>>> member = Color.RED
>>> member.name
'RED'
>>> member.value
1

Duplizieren von Enum-Mitgliedern und -Werten

Zwei Enum-Mitglieder mit demselben Namen sind ungültig.

>>> class Shape(Enum):
...     SQUARE = 2
...     SQUARE = 3
...
Traceback (most recent call last):
...
TypeError: 'SQUARE' already defined as 2

Ein Enum-Mitglied kann jedoch andere Namen haben. Bei zwei Einträgen A und B mit demselben Wert (und A zuerst definiert) ist B ein Alias für das Mitglied A. Ein Lookup nach Wert des Werts von A gibt das Mitglied A zurück. Ein Lookup nach Namen von A gibt das Mitglied A zurück. Ein Lookup nach Namen von B gibt ebenfalls das Mitglied A zurück.

>>> class Shape(Enum):
...     SQUARE = 2
...     DIAMOND = 1
...     CIRCLE = 3
...     ALIAS_FOR_SQUARE = 2
...
>>> Shape.SQUARE
<Shape.SQUARE: 2>
>>> Shape.ALIAS_FOR_SQUARE
<Shape.SQUARE: 2>
>>> Shape(2)
<Shape.SQUARE: 2>

Hinweis

Der Versuch, ein Mitglied mit demselben Namen wie ein bereits definiertes Attribut (ein anderes Mitglied, eine Methode usw.) zu erstellen, oder der Versuch, ein Attribut mit demselben Namen wie ein Mitglied zu erstellen, ist nicht erlaubt.

Sicherstellen eindeutiger Enumerationswerte

Standardmäßig erlauben Enumerationen mehrere Namen als Aliase für denselben Wert. Wenn dieses Verhalten nicht erwünscht ist, können Sie den Dekorator unique() verwenden.

>>> from enum import Enum, unique
>>> @unique
... class Mistake(Enum):
...     ONE = 1
...     TWO = 2
...     THREE = 3
...     FOUR = 3
...
Traceback (most recent call last):
...
ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE

Verwenden automatischer Werte

Wenn der genaue Wert unwichtig ist, können Sie auto verwenden.

>>> from enum import Enum, auto
>>> class Color(Enum):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> [member.value for member in Color]
[1, 2, 3]

Die Werte werden von _generate_next_value_() gewählt, die überschrieben werden kann.

>>> class AutoName(Enum):
...     @staticmethod
...     def _generate_next_value_(name, start, count, last_values):
...         return name
...
>>> class Ordinal(AutoName):
...     NORTH = auto()
...     SOUTH = auto()
...     EAST = auto()
...     WEST = auto()
...
>>> [member.value for member in Ordinal]
['NORTH', 'SOUTH', 'EAST', 'WEST']

Hinweis

Die Methode _generate_next_value_() muss vor allen Mitgliedern definiert werden.

Iteration

Das Iterieren über die Mitglieder eines Enums liefert nicht die Aliase.

>>> list(Shape)
[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
>>> list(Weekday)
[<Weekday.MONDAY: 1>, <Weekday.TUESDAY: 2>, <Weekday.WEDNESDAY: 4>, <Weekday.THURSDAY: 8>, <Weekday.FRIDAY: 16>, <Weekday.SATURDAY: 32>, <Weekday.SUNDAY: 64>]

Beachten Sie, dass die Aliase Shape.ALIAS_FOR_SQUARE und Weekday.WEEKEND nicht angezeigt werden.

Das spezielle Attribut __members__ ist eine schreibgeschützte geordnete Abbildung von Namen zu Mitgliedern. Es enthält alle in der Enumeration definierten Namen, einschließlich der Aliase.

>>> for name, member in Shape.__members__.items():
...     name, member
...
('SQUARE', <Shape.SQUARE: 2>)
('DIAMOND', <Shape.DIAMOND: 1>)
('CIRCLE', <Shape.CIRCLE: 3>)
('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>)

Das Attribut __members__ kann für detaillierten programmatischen Zugriff auf die Enumerationsmitglieder verwendet werden. Zum Beispiel das Finden aller Aliase.

>>> [name for name, member in Shape.__members__.items() if member.name != name]
['ALIAS_FOR_SQUARE']

Hinweis

Aliase für Flags umfassen Mitglieder mit mehreren gesetzten Flags, wie z. B. 3, und keine gesetzten Flags, d. h. 0.

Vergleiche

Enumerationsmitglieder werden nach Identität verglichen.

>>> Color.RED is Color.RED
True
>>> Color.RED is Color.BLUE
False
>>> Color.RED is not Color.BLUE
True

Geordnete Vergleiche zwischen Enumerationswerten werden nicht unterstützt. Enum-Mitglieder sind keine ganzen Zahlen (aber siehe IntEnum unten).

>>> Color.RED < Color.BLUE
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'Color' and 'Color'

Gleichheitsvergleiche sind jedoch definiert.

>>> Color.BLUE == Color.RED
False
>>> Color.BLUE != Color.RED
True
>>> Color.BLUE == Color.BLUE
True

Vergleiche mit Nicht-Enumerationswerten sind immer ungleich (auch hier wurde IntEnum ausdrücklich anders konzipiert, siehe unten).

>>> Color.BLUE == 2
False

Warnung

Es ist möglich, Module neu zu laden – wenn ein neu geladenes Modul Enums enthält, werden diese neu erstellt und die neuen Mitglieder vergleichen sich möglicherweise nicht identisch/gleich mit den ursprünglichen Mitgliedern.

Zugelassene Mitglieder und Attribute von Enumerationen

Die meisten der obigen Beispiele verwenden ganze Zahlen für Enumerationswerte. Die Verwendung von ganzen Zahlen ist kurz und praktisch (und wird standardmäßig von der Funktionalen API bereitgestellt), ist aber nicht strikt durchgesetzt. In den allermeisten Anwendungsfällen kümmert man sich nicht darum, was der tatsächliche Wert einer Enumeration ist. Aber wenn der Wert wichtig ist, können Enumerationen beliebige Werte haben.

Enumerationen sind Python-Klassen und können wie üblich Methoden und spezielle Methoden haben. Wenn wir diese Enumeration haben:

>>> class Mood(Enum):
...     FUNKY = 1
...     HAPPY = 3
...
...     def describe(self):
...         # self is the member here
...         return self.name, self.value
...
...     def __str__(self):
...         return 'my custom str! {0}'.format(self.value)
...
...     @classmethod
...     def favorite_mood(cls):
...         # cls here is the enumeration
...         return cls.HAPPY
...

Dann

>>> Mood.favorite_mood()
<Mood.HAPPY: 3>
>>> Mood.HAPPY.describe()
('HAPPY', 3)
>>> str(Mood.FUNKY)
'my custom str! 1'

Die Regeln für das, was erlaubt ist, sind wie folgt: Namen, die mit einem einzelnen Unterstrich beginnen und enden, sind von enum reserviert und dürfen nicht verwendet werden; alle anderen Attribute, die innerhalb einer Enumeration definiert sind, werden zu Mitgliedern dieser Enumeration, mit Ausnahme von speziellen Methoden (__str__(), __add__() usw.), Deskriptoren (Methoden sind ebenfalls Deskriptoren) und Variablennamen, die in _ignore_ aufgeführt sind.

Hinweis: Wenn Ihre Enumeration __new__() und/oder __init__() definiert, werden alle für das Enum-Mitglied angegebenen Werte an diese Methoden übergeben. Siehe Planet für ein Beispiel.

Hinweis

Die Methode __new__(), falls definiert, wird während der Erstellung der Enum-Mitglieder verwendet; sie wird dann durch die __new__() von Enum ersetzt, die nach der Klassenbildung zur Suche nach vorhandenen Mitgliedern verwendet wird. Siehe Wann __new__() vs. __init__() verwenden für weitere Details.

Eingeschränkte Enum-Unterklassenbildung

Eine neue Enum-Klasse muss eine Basis-Enum-Klasse, bis zu einen konkreten Datentyp und beliebig viele object-basierte Mixin-Klassen haben. Die Reihenfolge dieser Basisklassen ist:

class EnumName([mix-in, ...,] [data-type,] base-enum):
    pass

Außerdem ist die Unterklassenbildung einer Enumeration nur dann erlaubt, wenn die Enumeration keine Mitglieder definiert. Dies ist also verboten:

>>> class MoreColor(Color):
...     PINK = 17
...
Traceback (most recent call last):
...
TypeError: <enum 'MoreColor'> cannot extend <enum 'Color'>

Aber dies ist erlaubt:

>>> class Foo(Enum):
...     def some_behavior(self):
...         pass
...
>>> class Bar(Foo):
...     HAPPY = 1
...     SAD = 2
...

Die Unterklassenbildung von Enums, die Mitglieder definieren, würde zu einer Verletzung einiger wichtiger Invarianten von Typen und Instanzen führen. Andererseits ist es sinnvoll, ein gemeinsames Verhalten zwischen einer Gruppe von Enumerationen zu ermöglichen. (Siehe OrderedEnum für ein Beispiel.)

Dataclass-Unterstützung

Beim Erben von einer dataclass lässt die __repr__() den Namen der geerbten Klasse aus. Zum Beispiel:

>>> from dataclasses import dataclass, field
>>> @dataclass
... class CreatureDataMixin:
...     size: str
...     legs: int
...     tail: bool = field(repr=False, default=True)
...
>>> class Creature(CreatureDataMixin, Enum):
...     BEETLE = 'small', 6
...     DOG = 'medium', 4
...
>>> Creature.DOG
<Creature.DOG: size='medium', legs=4>

Verwenden Sie das dataclass()-Argument repr=False, um die Standard-repr() zu verwenden.

Geändert in Version 3.12: Nur die Dataclass-Felder werden im Wertebereich angezeigt, nicht der Name der Dataclass.

Hinweis

Das Hinzufügen des Dekorators dataclass() zu Enum und seinen Unterklassen wird nicht unterstützt. Es wird keine Fehler auslösen, aber es wird sehr seltsame Ergebnisse zur Laufzeit liefern, z. B. dass Mitglieder gleich zueinander sind.

>>> @dataclass               # don't do this: it does not make any sense
... class Color(Enum):
...    RED = 1
...    BLUE = 2
...
>>> Color.RED is Color.BLUE
False
>>> Color.RED == Color.BLUE  # problem is here: they should not be equal
True

Pickling

Enumerationen können gepickelt und entpickelt werden.

>>> from test.test_enum import Fruit
>>> from pickle import dumps, loads
>>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO))
True

Die üblichen Einschränkungen für das Pickling gelten: pickelbare Enums müssen im obersten Level eines Moduls definiert sein, da das Entpickeln erfordert, dass sie aus diesem Modul importierbar sind.

Hinweis

Mit Pickle-Protokollversion 4 ist es möglich, Enums, die in andere Klassen verschachtelt sind, einfach zu pickeln.

Es ist möglich zu ändern, wie Enum-Mitglieder gepickelt/entpickelt werden, indem __reduce_ex__() in der Enumerationsklasse definiert wird. Die Standardmethode ist per Wert, aber Enums mit komplizierten Werten möchten möglicherweise per Namen verwenden.

>>> import enum
>>> class MyEnum(enum.Enum):
...     __reduce_ex__ = enum.pickle_by_enum_name

Hinweis

Die Verwendung per Name für Flags wird nicht empfohlen, da unbenannte Aliase nicht entpickelt werden.

Funktionale API

Die Klasse Enum ist aufrufbar und bietet die folgende funktionale API.

>>> Animal = Enum('Animal', 'ANT BEE CAT DOG')
>>> Animal
<enum 'Animal'>
>>> Animal.ANT
<Animal.ANT: 1>
>>> list(Animal)
[<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]

Die Semantik dieser API ähnelt der von namedtuple. Das erste Argument des Aufrufs von Enum ist der Name der Enumeration.

Das zweite Argument ist die Quelle der Enumerationsmitgliedernamen. Es kann eine durch Leerzeichen getrennte Zeichenkette von Namen, eine Sequenz von Namen, eine Sequenz von 2-Tupeln mit Schlüssel/Wert-Paaren oder eine Zuordnung (z. B. ein Dictionary) von Namen zu Werten sein. Die letzten beiden Optionen ermöglichen die Zuweisung beliebiger Werte zu Enumerationen; die anderen weisen automatisch aufsteigende ganze Zahlen beginnend mit 1 zu (verwenden Sie den Parameter start, um einen anderen Startwert anzugeben). Eine neue Klasse, die von Enum abgeleitet ist, wird zurückgegeben. Mit anderen Worten, die obige Zuweisung zu Animal ist äquivalent zu:

>>> class Animal(Enum):
...     ANT = 1
...     BEE = 2
...     CAT = 3
...     DOG = 4
...

Der Grund für die Standardeinstellung auf 1 als Startnummer und nicht auf 0 ist, dass 0 im booleschen Sinne False ist, aber standardmäßig alle Enum-Mitglieder zu True ausgewertet werden.

Das Pickeln von Enums, die mit der funktionalen API erstellt wurden, kann knifflig sein, da Details der Frame-Stack-Implementierung verwendet werden, um zu versuchen, herauszufinden, in welchem Modul die Enumeration erstellt wird (z. B. schlägt dies fehl, wenn Sie eine Hilfsfunktion in einem separaten Modul verwenden, und funktioniert möglicherweise auch nicht auf IronPython oder Jython). Die Lösung besteht darin, den Modulnamen explizit wie folgt anzugeben:

>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)

Warnung

Wenn module nicht angegeben ist und Enum nicht feststellen kann, was es ist, werden die neuen Enum-Mitglieder nicht entpickelbar sein; um Fehler näher an der Quelle zu halten, wird das Pickling deaktiviert.

Das neue Pickle-Protokoll 4 hängt auch unter bestimmten Umständen davon ab, dass __qualname__ auf den Speicherort gesetzt wird, an dem Pickle die Klasse finden kann. Zum Beispiel, wenn die Klasse in class SomeData im globalen Geltungsbereich verfügbar gemacht wurde:

>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal')

Die vollständige Signatur ist:

Enum(
    value='NewEnumName',
    names=<...>,
    *,
    module='...',
    qualname='...',
    type=<mixed-in class>,
    start=1,
    )
  • value: Was die neue Enum-Klasse als ihren Namen aufzeichnen wird.

  • names: Die Enum-Mitglieder. Dies kann eine durch Leerzeichen oder Kommas getrennte Zeichenkette sein (Werte beginnen bei 1, sofern nicht anders angegeben).

    'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE'
    

    oder ein Iterator von Namen

    ['RED', 'GREEN', 'BLUE']
    

    oder ein Iterator von (Name, Wert)-Paaren

    [('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)]
    

    oder eine Zuordnung

    {'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42}
    
  • module: Name des Moduls, in dem die neue Enum-Klasse zu finden ist.

  • qualname: Wo im Modul die neue Enum-Klasse zu finden ist.

  • type: Typ, der in die neue Enum-Klasse gemischt werden soll.

  • start: Zahl, mit der gezählt werden soll, wenn nur Namen übergeben werden.

Geändert in Version 3.5: Der Parameter start wurde hinzugefügt.

Abgeleitete Enumerationen

IntEnum

Die erste Variante von Enum, die bereitgestellt wird, ist auch eine Unterklasse von int. Mitglieder einer IntEnum können mit ganzen Zahlen verglichen werden; folglich können auch ganzzahlige Enumerationen unterschiedlicher Typen miteinander verglichen werden.

>>> from enum import IntEnum
>>> class Shape(IntEnum):
...     CIRCLE = 1
...     SQUARE = 2
...
>>> class Request(IntEnum):
...     POST = 1
...     GET = 2
...
>>> Shape == 1
False
>>> Shape.CIRCLE == 1
True
>>> Shape.CIRCLE == Request.POST
True

Sie können jedoch immer noch nicht mit Standard-Enum-Enumerationen verglichen werden.

>>> class Shape(IntEnum):
...     CIRCLE = 1
...     SQUARE = 2
...
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...
>>> Shape.CIRCLE == Color.RED
False

IntEnum-Werte verhalten sich in anderer Hinsicht wie ganze Zahlen, wie Sie es erwarten würden.

>>> int(Shape.CIRCLE)
1
>>> ['a', 'b', 'c'][Shape.CIRCLE]
'b'
>>> [i for i in range(Shape.SQUARE)]
[0, 1]

StrEnum

Die zweite Variante von Enum, die bereitgestellt wird, ist auch eine Unterklasse von str. Mitglieder einer StrEnum können mit Zeichenketten verglichen werden; folglich können auch Zeichenketten-Enumerationen unterschiedlicher Typen miteinander verglichen werden.

Hinzugefügt in Version 3.11.

IntFlag

Die nächste Variante von Enum, die bereitgestellt wird, IntFlag, basiert ebenfalls auf int. Der Unterschied besteht darin, dass IntFlag-Mitglieder mit den bitweisen Operatoren (&, |, ^, ~) kombiniert werden können und das Ergebnis immer noch ein IntFlag-Mitglied ist, wenn möglich. Wie IntEnum sind IntFlag-Mitglieder ebenfalls ganze Zahlen und können überall verwendet werden, wo eine int verwendet wird.

Hinweis

Jede Operation auf einem IntFlag-Mitglied außer den bitweisen Operationen verliert die IntFlag-Mitgliedschaft.

Bitweise Operationen, die zu ungültigen IntFlag-Werten führen, verlieren die IntFlag-Mitgliedschaft. Siehe FlagBoundary für Details.

Hinzugefügt in Version 3.6.

Geändert in Version 3.11.

Beispiel für eine IntFlag-Klasse

>>> from enum import IntFlag
>>> class Perm(IntFlag):
...     R = 4
...     W = 2
...     X = 1
...
>>> Perm.R | Perm.W
<Perm.R|W: 6>
>>> Perm.R + Perm.W
6
>>> RW = Perm.R | Perm.W
>>> Perm.R in RW
True

Es ist auch möglich, die Kombinationen zu benennen.

>>> class Perm(IntFlag):
...     R = 4
...     W = 2
...     X = 1
...     RWX = 7
...
>>> Perm.RWX
<Perm.RWX: 7>
>>> ~Perm.RWX
<Perm: 0>
>>> Perm(7)
<Perm.RWX: 7>

Hinweis

Benannte Kombinationen werden als Aliase betrachtet. Aliase werden bei der Iteration nicht angezeigt, können aber bei Lookups nach Wert zurückgegeben werden.

Geändert in Version 3.11.

Ein weiterer wichtiger Unterschied zwischen IntFlag und Enum ist, dass die boolesche Auswertung False ist, wenn keine Flags gesetzt sind (der Wert ist 0).

>>> Perm.R & Perm.X
<Perm: 0>
>>> bool(Perm.R & Perm.X)
False

Da IntFlag-Mitglieder auch Unterklassen von int sind, können sie mit diesen kombiniert werden (können aber die IntFlag-Mitgliedschaft verlieren).

>>> Perm.X | 4
<Perm.R|X: 5>

>>> Perm.X + 8
9

Hinweis

Der Negationsoperator ~ gibt immer ein IntFlag-Mitglied mit einem positiven Wert zurück.

>>> (~Perm.X).value == (Perm.R|Perm.W).value == 6
True

IntFlag-Mitglieder können ebenfalls iteriert werden.

>>> list(RW)
[<Perm.R: 4>, <Perm.W: 2>]

Hinzugefügt in Version 3.11.

Flag

Die letzte Variante ist Flag. Wie IntFlag können Flag-Mitglieder mit den bitweisen Operatoren (&, |, ^, ~) kombiniert werden. Im Gegensatz zu IntEnum können sie nicht mit anderen Flag-Enumerationen oder mit int kombiniert oder verglichen werden. Obwohl es möglich ist, die Werte direkt anzugeben, wird empfohlen, auto als Wert zu verwenden und Flag einen geeigneten Wert auswählen zu lassen.

Hinzugefügt in Version 3.6.

Wie bei IntFlag ist die boolesche Auswertung False, wenn eine Kombination von Flag-Mitgliedern dazu führt, dass keine Flags gesetzt sind.

>>> from enum import Flag, auto
>>> class Color(Flag):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.RED & Color.GREEN
<Color: 0>
>>> bool(Color.RED & Color.GREEN)
False

Einzelne Flags sollten Werte haben, die Potenzen von zwei sind (1, 2, 4, 8, ...), während Kombinationen von Flags dies nicht tun.

>>> class Color(Flag):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...     WHITE = RED | BLUE | GREEN
...
>>> Color.WHITE
<Color.WHITE: 7>

Das Benennen der Bedingung "keine Flags gesetzt" ändert ihren booleschen Wert nicht.

>>> class Color(Flag):
...     BLACK = 0
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.BLACK
<Color.BLACK: 0>
>>> bool(Color.BLACK)
False

Flag-Mitglieder können ebenfalls iteriert werden.

>>> purple = Color.RED | Color.BLUE
>>> list(purple)
[<Color.RED: 1>, <Color.BLUE: 2>]

Hinzugefügt in Version 3.11.

Hinweis

Für den Großteil neuer Codes werden Enum und Flag dringend empfohlen, da IntEnum und IntFlag einige semantische Versprechen einer Enumeration brechen (indem sie mit ganzen Zahlen vergleichbar sind und somit transitiv mit anderen, nicht zusammenhängenden Enumerationen). IntEnum und IntFlag sollten nur in Fällen verwendet werden, in denen Enum und Flag nicht ausreichen; zum Beispiel, wenn Ganzzahlkonstanten durch Enumerationen ersetzt werden oder für die Interoperabilität mit anderen Systemen.

Andere

Obwohl IntEnum Teil des enum-Moduls ist, wäre es sehr einfach, es unabhängig zu implementieren.

class IntEnum(int, ReprEnum):   # or Enum instead of ReprEnum
    pass

Dies zeigt, wie ähnliche abgeleitete Aufzählungen definiert werden können; zum Beispiel eine FloatEnum, die float anstelle von int einmischt.

Einige Regeln

  1. Beim Unterklassenbildung von Enum müssen Mix-in-Typen in der Basissequenz vor der Enum-Klasse selbst erscheinen, wie im obigen IntEnum-Beispiel.

  2. Mix-in-Typen müssen unterklassifizierbar sein. Zum Beispiel sind bool und range nicht unterklassifizierbar und lösen bei der Enum-Erstellung einen Fehler aus, wenn sie als Mix-in-Typ verwendet werden.

  3. Obwohl Enum Mitglieder beliebigen Typs haben kann, sobald Sie einen zusätzlichen Typ einmischen, müssen alle Mitglieder Werte dieses Typs haben, z. B. int oben. Diese Einschränkung gilt nicht für Mix-ins, die nur Methoden hinzufügen und keinen anderen Typ angeben.

  4. Wenn ein anderer Datentyp eingemischt wird, ist das Attribut value *nicht dasselbe* wie das Enum-Mitglied selbst, obwohl es äquivalent ist und gleich verglichen wird.

  5. Ein Datentyp ist ein Mix-in, das __new__() definiert, oder ein dataclass

  6. %-Formatierung: %s und %r rufen die __str__()- und __repr__()-Methoden der Enum-Klasse auf; andere Codes (wie %i oder %h für IntEnum) behandeln das Enum-Mitglied als seinen eingemischten Typ.

  7. Formatierte Zeichenkettenliterale (f-strings), str.format() und format() verwenden die __str__()-Methode des Enums.

Hinweis

Da IntEnum, IntFlag und StrEnum als Drop-in-Ersatz für bestehende Konstanten konzipiert sind, wurde ihre __str__()-Methode auf die __str__()-Methode ihrer Datentypen zurückgesetzt.

Wann __new__() vs. __init__() verwenden

__new__() muss verwendet werden, wenn Sie den tatsächlichen Wert des Enum-Mitglieds anpassen möchten. Alle anderen Modifikationen können entweder in __new__() oder __init__() erfolgen, wobei __init__() bevorzugt wird.

Zum Beispiel, wenn Sie mehrere Elemente an den Konstruktor übergeben möchten, aber nur eines davon der Wert sein soll

>>> class Coordinate(bytes, Enum):
...     """
...     Coordinate with binary codes that can be indexed by the int code.
...     """
...     def __new__(cls, value, label, unit):
...         obj = bytes.__new__(cls, [value])
...         obj._value_ = value
...         obj.label = label
...         obj.unit = unit
...         return obj
...     PX = (0, 'P.X', 'km')
...     PY = (1, 'P.Y', 'km')
...     VX = (2, 'V.X', 'km/s')
...     VY = (3, 'V.Y', 'km/s')
...

>>> print(Coordinate['PY'])
Coordinate.PY

>>> print(Coordinate(3))
Coordinate.VY

Warnung

Rufen Sie *nicht* super().__new__() auf, da das nur nachschlagefähige __new__ das ist, das gefunden wird; verwenden Sie stattdessen direkt den Datentyp.

Feinere Punkte

Unterstützte __dunder__-Namen

__members__ ist eine schreibgeschützte, geordnete Zuordnung von member_name:member-Elementen. Es ist nur auf der Klasse verfügbar.

__new__(), falls angegeben, muss die Enum-Mitglieder erstellen und zurückgeben; es ist auch sehr ratsam, den _value_ des Mitglieds entsprechend zu setzen. Sobald alle Mitglieder erstellt sind, wird es nicht mehr verwendet.

Unterstützte _sunder_-Namen

  • _name_ – Name des Mitglieds

  • _value_ – Wert des Mitglieds; kann in __new__ gesetzt werden

  • _missing_() – eine Nachschlagefunktion, die verwendet wird, wenn ein Wert nicht gefunden wird; kann überschrieben werden

  • _ignore_ – eine Liste von Namen, entweder als list oder als str, die nicht in Mitglieder umgewandelt werden und aus der endgültigen Klasse entfernt werden

  • _generate_next_value_() – wird verwendet, um einen geeigneten Wert für ein Enum-Mitglied zu erhalten; kann überschrieben werden

  • _add_alias_() – fügt einen neuen Namen als Alias zu einem bestehenden Mitglied hinzu.

  • _add_value_alias_() – fügt einen neuen Wert als Alias zu einem bestehenden Mitglied hinzu. Siehe MultiValueEnum für ein Beispiel.

    Hinweis

    Für Standard- Enum-Klassen ist der nächste gewählte Wert der höchste gesehene Wert, inkrementiert um eins.

    Für Flag-Klassen ist der nächste gewählte Wert die nächste höhere Zweierpotenz.

    Geändert in Version 3.13: Frühere Versionen verwendeten den letzten gesehenen Wert anstelle des höchsten Werts.

Hinzugefügt in Version 3.6: _missing_, _order_, _generate_next_value_

Hinzugefügt in Version 3.7: _ignore_

Hinzugefügt in Version 3.13: _add_alias_, _add_value_alias_

Um Python 2 / Python 3 Code synchron zu halten, kann ein Attribut _order_ bereitgestellt werden. Es wird mit der tatsächlichen Reihenfolge der Aufzählung verglichen und löst einen Fehler aus, wenn die beiden nicht übereinstimmen.

>>> class Color(Enum):
...     _order_ = 'RED GREEN BLUE'
...     RED = 1
...     BLUE = 3
...     GREEN = 2
...
Traceback (most recent call last):
...
TypeError: member order does not match _order_:
  ['RED', 'BLUE', 'GREEN']
  ['RED', 'GREEN', 'BLUE']

Hinweis

In Python 2 Code ist das Attribut _order_ notwendig, da die Definitionsreihenfolge verloren geht, bevor sie aufgezeichnet werden kann.

Private Namen

Private Namen werden nicht in Enum-Mitglieder umgewandelt, sondern bleiben normale Attribute.

Geändert in Version 3.11.

Enum-Mitgliedstyp

Enum-Mitglieder sind Instanzen ihrer Enum-Klasse und werden normalerweise als EnumClass.member zugegriffen. In bestimmten Situationen, wie z. B. beim Schreiben von benutzerdefiniertem Enum-Verhalten, ist der direkte Zugriff auf ein Mitglied von einem anderen nützlich und wird unterstützt; um Namenskonflikte zwischen Mitgliedsnamen und Attributen/Methoden von eingemischten Klassen zu vermeiden, werden Großbuchstaben jedoch dringend empfohlen.

Geändert in Version 3.5.

Erstellen von Mitgliedern, die mit anderen Datentypen gemischt sind

Beim Unterklassenbildung anderer Datentypen, wie int oder str, mit einem Enum werden alle Werte nach dem = an den Konstruktor dieses Datentyps übergeben. Zum Beispiel

>>> class MyEnum(IntEnum):      # help(int) -> int(x, base=10) -> integer
...     example = '11', 16      # so x='11' and base=16
...
>>> MyEnum.example.value        # and hex(11) is...
17

Boolescher Wert von Enum-Klassen und -Mitgliedern

Enum-Klassen, die mit Nicht-Enum-Typen (wie int, str usw.) gemischt sind, werden gemäß den Regeln des eingemischten Typs ausgewertet; andernfalls werden alle Mitglieder als True ausgewertet. Um die boolesche Auswertung Ihrer eigenen Enum vom Wert des Mitglieds abhängig zu machen, fügen Sie Ihrer Klasse Folgendes hinzu:

def __bool__(self):
    return bool(self.value)

Einfache Enum-Klassen werden immer als True ausgewertet.

Enum-Klassen mit Methoden

Wenn Sie Ihrer Enum-Unterklasse zusätzliche Methoden geben, wie die Planet-Klasse unten, erscheinen diese Methoden in einem dir() des Mitglieds, aber nicht der Klasse.

>>> dir(Planet)
['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
>>> dir(Planet.EARTH)
['__class__', '__doc__', '__module__', 'mass', 'name', 'radius', 'surface_gravity', 'value']

Kombinieren von Mitgliedern von Flag

Beim Iterieren über eine Kombination von Flag-Mitgliedern werden nur die Mitglieder zurückgegeben, die aus einem einzigen Bit bestehen.

>>> class Color(Flag):
...     RED = auto()
...     GREEN = auto()
...     BLUE = auto()
...     MAGENTA = RED | BLUE
...     YELLOW = RED | GREEN
...     CYAN = GREEN | BLUE
...
>>> Color(3)  # named combination
<Color.YELLOW: 3>
>>> Color(7)      # not named combination
<Color.RED|GREEN|BLUE: 7>

Flag und IntFlag Details

Verwendung des folgenden Snippets für unsere Beispiele

>>> class Color(IntFlag):
...     BLACK = 0
...     RED = 1
...     GREEN = 2
...     BLUE = 4
...     PURPLE = RED | BLUE
...     WHITE = RED | GREEN | BLUE
...

gelten folgende Aussagen

  • Ein-Bit-Flags sind kanonisch

  • Mehr-Bit- und Null-Bit-Flags sind Aliase

  • Nur kanonische Flags werden bei der Iteration zurückgegeben

    >>> list(Color.WHITE)
    [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
    
  • Das Negieren eines Flags oder einer Flag-Menge gibt eine neue Flag/Flag-Menge mit dem entsprechenden positiven ganzzahligen Wert zurück

    >>> Color.BLUE
    <Color.BLUE: 4>
    
    >>> ~Color.BLUE
    <Color.RED|GREEN: 3>
    
  • Namen von Pseudo-Flags werden aus den Namen ihrer Mitglieder konstruiert

    >>> (Color.RED | Color.GREEN).name
    'RED|GREEN'
    
    >>> class Perm(IntFlag):
    ...     R = 4
    ...     W = 2
    ...     X = 1
    ...
    >>> (Perm.R & Perm.W).name is None  # effectively Perm(0)
    True
    
  • Mehr-Bit-Flags, auch Aliase genannt, können aus Operationen zurückgegeben werden

    >>> Color.RED | Color.BLUE
    <Color.PURPLE: 5>
    
    >>> Color(7)  # or Color(-1)
    <Color.WHITE: 7>
    
    >>> Color(0)
    <Color.BLACK: 0>
    
  • Mitgliedschaft / Containment-Prüfung: Nullwertige Flags werden immer als enthalten betrachtet

    >>> Color.BLACK in Color.WHITE
    True
    

    Andernfalls wird nur dann True zurückgegeben, wenn alle Bits eines Flags im anderen Flag enthalten sind.

    >>> Color.PURPLE in Color.WHITE
    True
    
    >>> Color.GREEN in Color.PURPLE
    False
    

Es gibt einen neuen Grenzwertmechanismus, der steuert, wie Out-of-Range-/ungültige Bits behandelt werden: STRICT, CONFORM, EJECT und KEEP

  • STRICT -> löst eine Ausnahme aus, wenn ungültige Werte vorhanden sind

  • CONFORM -> verwirft alle ungültigen Bits

  • EJECT -> verlieren den Flag-Status und werden zu einem normalen int mit dem gegebenen Wert

  • KEEP -> behalte die zusätzlichen Bits

    • behält den Flag-Status und zusätzliche Bits

    • Zusätzliche Bits erscheinen nicht in der Iteration

    • Zusätzliche Bits erscheinen in repr() und str()

Der Standard für Flag ist STRICT, der Standard für IntFlag ist EJECT, und der Standard für _convert_ ist KEEP (siehe ssl.Options für ein Beispiel, wann KEEP benötigt wird).

Wie unterscheiden sich Enums und Flags?

Enums haben eine benutzerdefinierte Metaklasse, die viele Aspekte sowohl abgeleiteter Enum-Klassen als auch ihrer Instanzen (Mitglieder) beeinflusst.

Enum-Klassen

Die Metaklasse EnumType ist verantwortlich für die Bereitstellung von __contains__(), __dir__(), __iter__() und anderen Methoden, die es erlauben, Dinge mit einer Enum-Klasse zu tun, die bei einer typischen Klasse fehlschlagen, wie z. B. list(Color) oder some_enum_var in Color. EnumType ist dafür verantwortlich, sicherzustellen, dass verschiedene andere Methoden der endgültigen Enum-Klasse korrekt sind (wie z. B. __new__(), __getnewargs__(), __str__() und __repr__()).

Flag-Klassen

Flags haben eine erweiterte Sicht auf Aliasing: Um kanonisch zu sein, muss der Wert eines Flags eine Zweierpotenz sein und kein Duplikatname. Zusätzlich zur Enum-Definition von Alias gilt ein Flag ohne Wert (auch 0 genannt) oder mit mehr als einer Zweierpotenz (z. B. 3) als Alias.

Enum-Mitglieder (auch Instanzen genannt)

Das Interessanteste an Enum-Mitgliedern ist, dass sie Singletons sind. EnumType erstellt sie alle während der Erstellung der Enum-Klasse selbst und setzt dann ein benutzerdefiniertes __new__() ein, um sicherzustellen, dass nie neue instanziiert werden, indem nur die vorhandenen Mitgliedsinstanzen zurückgegeben werden.

Flag-Mitglieder

Flag-Mitglieder können wie die Flag-Klasse iteriert werden, und nur die kanonischen Mitglieder werden zurückgegeben. Zum Beispiel

>>> list(Color)
[<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]

(Beachten Sie, dass BLACK, PURPLE und WHITE nicht angezeigt werden.)

Das Invertieren eines Flag-Mitglieds gibt den entsprechenden positiven Wert zurück, anstatt eines negativen Wertes - zum Beispiel

>>> ~Color.RED
<Color.GREEN|BLUE: 6>

Flag-Mitglieder haben eine Länge, die der Anzahl der von ihnen enthaltenen Zweierpotenzwerte entspricht. Zum Beispiel

>>> len(Color.PURPLE)
2

Enum-Kochbuch

Während Enum, IntEnum, StrEnum, Flag und IntFlag die Mehrheit der Anwendungsfälle abdecken sollten, können sie nicht alle abdecken. Hier sind Rezepte für einige verschiedene Arten von Aufzählungen, die direkt oder als Beispiele für die Erstellung eigener verwendet werden können.

Werte weglassen

In vielen Anwendungsfällen ist es egal, was der tatsächliche Wert einer Aufzählung ist. Es gibt mehrere Möglichkeiten, diesen einfachen Aufzählungstyp zu definieren

  • Verwendung von Instanzen von auto für den Wert

  • Verwendung von Instanzen von object als Wert

  • Verwendung einer beschreibenden Zeichenkette als Wert

  • Verwendung eines Tupels als Wert und eines benutzerdefinierten __new__(), um das Tupel durch einen int-Wert zu ersetzen.

Die Verwendung einer dieser Methoden signalisiert dem Benutzer, dass diese Werte nicht wichtig sind, und ermöglicht es auch, Mitglieder hinzuzufügen, zu entfernen oder neu zu ordnen, ohne die verbleibenden Mitglieder neu nummerieren zu müssen.

Verwendung von auto

Die Verwendung von auto würde so aussehen

>>> class Color(Enum):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.GREEN
<Color.GREEN: 3>

Verwendung von object

Die Verwendung von object würde so aussehen

>>> class Color(Enum):
...     RED = object()
...     GREEN = object()
...     BLUE = object()
...
>>> Color.GREEN
<Color.GREEN: <object object at 0x...>>

Dies ist auch ein gutes Beispiel dafür, warum Sie Ihr eigenes __repr__() schreiben möchten.

>>> class Color(Enum):
...     RED = object()
...     GREEN = object()
...     BLUE = object()
...     def __repr__(self):
...         return "<%s.%s>" % (self.__class__.__name__, self._name_)
...
>>> Color.GREEN
<Color.GREEN>

Verwendung einer beschreibenden Zeichenkette

Die Verwendung einer Zeichenkette als Wert würde so aussehen

>>> class Color(Enum):
...     RED = 'stop'
...     GREEN = 'go'
...     BLUE = 'too fast!'
...
>>> Color.GREEN
<Color.GREEN: 'go'>

Verwendung eines benutzerdefinierten __new__()

Die Verwendung eines automatisch nummerierenden __new__() würde so aussehen

>>> class AutoNumber(Enum):
...     def __new__(cls):
...         value = len(cls.__members__) + 1
...         obj = object.__new__(cls)
...         obj._value_ = value
...         return obj
...
>>> class Color(AutoNumber):
...     RED = ()
...     GREEN = ()
...     BLUE = ()
...
>>> Color.GREEN
<Color.GREEN: 2>

Um ein allgemeineres AutoNumber zu erstellen, fügen Sie *args zur Signatur hinzu

>>> class AutoNumber(Enum):
...     def __new__(cls, *args):      # this is the only change from above
...         value = len(cls.__members__) + 1
...         obj = object.__new__(cls)
...         obj._value_ = value
...         return obj
...

Wenn Sie dann von AutoNumber erben, können Sie Ihre eigene __init__ schreiben, um zusätzliche Argumente zu verarbeiten.

>>> class Swatch(AutoNumber):
...     def __init__(self, pantone='unknown'):
...         self.pantone = pantone
...     AUBURN = '3497'
...     SEA_GREEN = '1246'
...     BLEACHED_CORAL = () # New color, no Pantone code yet!
...
>>> Swatch.SEA_GREEN
<Swatch.SEA_GREEN: 2>
>>> Swatch.SEA_GREEN.pantone
'1246'
>>> Swatch.BLEACHED_CORAL.pantone
'unknown'

Hinweis

Die Methode __new__(), falls definiert, wird während der Erstellung der Enum-Mitglieder verwendet; sie wird dann durch das __new__() von Enum ersetzt, das nach der Klassenerstellung für die Suche nach vorhandenen Mitgliedern verwendet wird.

Warnung

Rufen Sie *nicht* super().__new__() auf, da das nur nachschlagefähige __new__ das ist, das gefunden wird; verwenden Sie stattdessen direkt den Datentyp - z. B.

obj = int.__new__(cls, value)

OrderedEnum

Eine geordnete Aufzählung, die nicht auf IntEnum basiert und daher die normalen Enum-Invarianten beibehält (z. B. nicht mit anderen Aufzählungen vergleichbar ist).

>>> class OrderedEnum(Enum):
...     def __ge__(self, other):
...         if self.__class__ is other.__class__:
...             return self.value >= other.value
...         return NotImplemented
...     def __gt__(self, other):
...         if self.__class__ is other.__class__:
...             return self.value > other.value
...         return NotImplemented
...     def __le__(self, other):
...         if self.__class__ is other.__class__:
...             return self.value <= other.value
...         return NotImplemented
...     def __lt__(self, other):
...         if self.__class__ is other.__class__:
...             return self.value < other.value
...         return NotImplemented
...
>>> class Grade(OrderedEnum):
...     A = 5
...     B = 4
...     C = 3
...     D = 2
...     F = 1
...
>>> Grade.C < Grade.A
True

DuplicateFreeEnum

Löst einen Fehler aus, wenn ein duplizierter Mitgliedswert gefunden wird, anstatt einen Alias zu erstellen.

>>> class DuplicateFreeEnum(Enum):
...     def __init__(self, *args):
...         cls = self.__class__
...         if any(self.value == e.value for e in cls):
...             a = self.name
...             e = cls(self.value).name
...             raise ValueError(
...                 "aliases not allowed in DuplicateFreeEnum:  %r --> %r"
...                 % (a, e))
...
>>> class Color(DuplicateFreeEnum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3
...     GRENE = 2
...
Traceback (most recent call last):
  ...
ValueError: aliases not allowed in DuplicateFreeEnum:  'GRENE' --> 'GREEN'

Hinweis

Dies ist ein nützliches Beispiel für die Unterklassenbildung von Enum, um andere Verhaltensweisen hinzuzufügen oder zu ändern sowie Aliase zu verbieten. Wenn nur das Verbot von Aliassen gewünscht ist, kann stattdessen der Dekorator unique() verwendet werden.

MultiValueEnum

Unterstützt mehr als einen Wert pro Mitglied.

>>> class MultiValueEnum(Enum):
...     def __new__(cls, value, *values):
...         self = object.__new__(cls)
...         self._value_ = value
...         for v in values:
...             self._add_value_alias_(v)
...         return self
...
>>> class DType(MultiValueEnum):
...     float32 = 'f', 8
...     double64 = 'd', 9
...
>>> DType('f')
<DType.float32: 'f'>
>>> DType(9)
<DType.double64: 'd'>

Planet

Wenn __new__() oder __init__() definiert ist, wird der Wert des Enum-Mitglieds an diese Methoden übergeben.

>>> class Planet(Enum):
...     MERCURY = (3.303e+23, 2.4397e6)
...     VENUS   = (4.869e+24, 6.0518e6)
...     EARTH   = (5.976e+24, 6.37814e6)
...     MARS    = (6.421e+23, 3.3972e6)
...     JUPITER = (1.9e+27,   7.1492e7)
...     SATURN  = (5.688e+26, 6.0268e7)
...     URANUS  = (8.686e+25, 2.5559e7)
...     NEPTUNE = (1.024e+26, 2.4746e7)
...     def __init__(self, mass, radius):
...         self.mass = mass       # in kilograms
...         self.radius = radius   # in meters
...     @property
...     def surface_gravity(self):
...         # universal gravitational constant  (m3 kg-1 s-2)
...         G = 6.67300E-11
...         return G * self.mass / (self.radius * self.radius)
...
>>> Planet.EARTH.value
(5.976e+24, 6378140.0)
>>> Planet.EARTH.surface_gravity
9.802652743337129

TimePeriod

Ein Beispiel zur Veranschaulichung der Verwendung des Attributs _ignore_.

>>> from datetime import timedelta
>>> class Period(timedelta, Enum):
...     "different lengths of time"
...     _ignore_ = 'Period i'
...     Period = vars()
...     for i in range(367):
...         Period['day_%d' % i] = i
...
>>> list(Period)[:2]
[<Period.day_0: datetime.timedelta(0)>, <Period.day_1: datetime.timedelta(days=1)>]
>>> list(Period)[-2:]
[<Period.day_365: datetime.timedelta(days=365)>, <Period.day_366: datetime.timedelta(days=366)>]

Unterklassenbildung von EnumType

Während die meisten Enum-Anforderungen durch die Anpassung von Enum-Unterklassen erfüllt werden können, entweder mit Klassen-Dekoratoren oder benutzerdefinierten Funktionen, kann EnumType unterklassifiziert werden, um eine andere Enum-Erfahrung zu bieten.