Enum-HOWTO
**********

Ein "Enum" ist eine Menge symbolischer Namen, die an eindeutige Werte
gebunden sind. Sie sind globalen Variablen ähnlich, bieten aber ein
nützlicheres "repr()", Gruppierung, Typsicherheit und ein paar weitere
Eigenschaften.

Am nützlichsten sind sie, wenn du eine Variable hast, die einen aus
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-Grundfarben:

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

Wie du siehst, ist ein "Enum" zu erzeugen so einfach wie eine Klasse
zu schreiben, die von "Enum" selbst erbt.

Bemerkung:

  Schreibweise von Enum-MitgliedernWeil Enums Konstanten darstellen
  und weil es Probleme mit Namenskollisionen zwischen Methoden und
  Attributen von Mixin-Klassen und Enum-Namen vermeiden hilft,
  empfehlen wir dringend GROSSBUCHSTABEN-Namen für Mitglieder und
  verwenden diesen Stil in unseren Beispielen.

Je nach Art des Enums kann der Wert eines Mitglieds wichtig sein oder
nicht, aber in beiden Fällen lässt sich dieser Wert verwenden, um das
zugehörige Mitglied zu bekommen:

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

Wie du siehst, zeigt das "repr()" eines Mitglieds den Enum-Namen, den
Mitgliedsnamen und den Wert. Das "str()" eines Mitglieds zeigt nur den
Enum-Namen und den Mitgliedsnamen:

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

Der *Typ* eines Aufzählungsmitglieds 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

Anders als viele Sprachen, die Aufzählungen ausschließlich als Name-
Wert-Paare behandeln, können Python-Enums Verhalten dazubekommen. So
hat "datetime.date" zum Beispiel zwei Methoden, die den Wochentag
zurückgeben: "weekday()" und "isoweekday()". Der Unterschied ist, dass
die eine von 0 bis 6 und die andere von 1 bis 7 zählt. Anstatt das
selbst im Kopf zu behalten, können wir dem Enum "Weekday" eine Methode
hinzufügen, die den Tag aus der "date"-Instanz zieht und das passende
Enum-Mitglied zurückgibt:

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

Das vollständige Enum "Weekday" 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! Sieh her:

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

Wenn du das natürlich an einem anderen Tag liest, siehst du
stattdessen diesen Tag.

Dieses Enum "Weekday" ist großartig, wenn unsere Variable nur einen
Tag braucht – aber was, wenn wir mehrere brauchen? Vielleicht
schreiben wir eine Funktion, die Aufgaben über eine Woche verteilt,
und wollen keine "list" verwenden – wir könnten eine andere Art von
"Enum" nehmen:

   >>> 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 alle Potenzen von 2.

Genau wie beim ursprünglichen Enum "Weekday" oben können wir eine
einzelne Auswahl treffen:

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

Aber "Flag" erlaubt es uns außerdem, mehrere Mitglieder in einer
einzigen Variablen zu verbinden:

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

Du kannst über eine "Flag"-Variable sogar iterieren:

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

Gut, 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, die die Aufgaben für einen gegebenen Tag anzeigt:

   >>> 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, kannst du dir Arbeit sparen und für die Werte "auto()"
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


Programmgesteuerter Zugriff auf Aufzählungsmitglieder und ihre Attribute
========================================================================

Manchmal ist es nützlich, auf Mitglieder von Aufzählungen
programmgesteuert zuzugreifen (also in Lagen, in denen "Color.RED"
nicht in Frage kommt, weil die genaue Farbe zum Zeitpunkt des
Programmierens nicht bekannt ist). "Enum" erlaubt solchen Zugriff:

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

Wenn du über den *Namen* auf Enum-Mitglieder zugreifen willst,
verwende den Zugriff per Index:

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

Wenn du ein Enum-Mitglied hast und seinen "name" oder "value"
brauchst:

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


Enum-Mitglieder und -Werte doppeln
==================================

Zwei Enum-Mitglieder mit demselben Namen zu haben ist unzulässig:

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

Einem Enum-Mitglied können aber andere Namen zugeordnet sein. Gegeben
seien zwei Einträge "A" und "B" mit demselben Wert (und "A" zuerst
definiert). Dann ist "B" ein Alias für das Mitglied "A". Ein
Nachschlagen des Werts von "A" über den Wert gibt das Mitglied "A"
zurück. Ein Nachschlagen von "A" über den Namen gibt das Mitglied "A"
zurück. Ein Nachschlagen von "B" über den Namen 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>

Bemerkung:

  Zu versuchen, ein Mitglied mit demselben Namen wie ein bereits
  definiertes Attribut (ein anderes Mitglied, eine Methode usw.) zu
  erzeugen, oder ein Attribut mit demselben Namen wie ein Mitglied zu
  erzeugen, ist nicht erlaubt.


Eindeutige Aufzählungswerte sicherstellen
=========================================

Standardmäßig erlauben Aufzählungen mehrere Namen als Aliase für
denselben Wert. Ist dieses Verhalten nicht gewünscht, kannst du den
Decorator "@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


Automatische Werte verwenden
============================

Ist der genaue Wert unwichtig, kannst du "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, das sich
überschreiben lässt:

   >>> 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']

Bemerkung:

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


Iteration
=========

Beim Iterieren über die Mitglieder eines Enums kommen die Aliase nicht
vor:

   >>> 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>]

Beachte, dass die Aliase "Shape.ALIAS_FOR_SQUARE" und
"Weekday.WEEKEND" nicht gezeigt werden.

Das besondere Attribut "__members__" ist eine nur lesbare, geordnete
Abbildung von Namen auf Mitglieder. Es umfasst alle in der Aufzählung
definierten Namen, auch die 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__" lässt sich für einen ausführlichen
programmgesteuerten Zugriff auf die Aufzählungsmitglieder verwenden –
zum Beispiel, um alle Aliase zu finden:

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

Bemerkung:

  Zu den Aliasen bei Flags gehören Werte mit mehreren gesetzten Flags,
  etwa "3", und mit keinem gesetzten Flag, also "0".


Vergleiche
==========

Aufzählungsmitglieder werden über die Identität verglichen:

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

Ordnende Vergleiche zwischen Aufzählungswerten werden *nicht*
unterstützt. Enum-Mitglieder sind keine Ganzzahlen (siehe aber 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 allerdings definiert:

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

Gleichheitsvergleiche mit Werten, die keine Aufzählungen sind, geben
immer "False" zurück (auch hier gilt: "IntEnum" wurde ausdrücklich
dafür entworfen, sich anders zu verhalten, siehe unten):

   >>> Color.BLUE == 2
   False

Warnung:

  Es ist möglich, Module neu zu laden – enthält ein neu geladenes
  Modul Enums, werden sie neu erzeugt, und die neuen Mitglieder sind
  möglicherweise nicht identisch beziehungsweise gleich zu den
  ursprünglichen Mitgliedern.


Erlaubte Mitglieder und Attribute von Aufzählungen
==================================================

Die meisten Beispiele oben verwenden Ganzzahlen als Aufzählungswerte.
Ganzzahlen zu verwenden ist kurz und praktisch (und wird von der
Functional API standardmäßig geliefert), ist aber nicht streng
vorgeschrieben. In der weit überwiegenden Zahl der Anwendungsfälle ist
es einem gleichgültig, welchen Wert eine Aufzählung tatsächlich hat.
Ist der Wert aber *wichtig*, können Aufzählungen beliebige Werte
haben.

Aufzählungen sind Python-Klassen und können wie üblich Methoden und
besondere Methoden haben. Wenn wir diese Aufzählung 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 Erlaubte lauten: Namen, die mit einem einzelnen
Unterstrich beginnen und enden, sind für enum reserviert und dürfen
nicht verwendet werden. Alle anderen innerhalb einer Aufzählung
definierten Attribute werden zu Mitgliedern dieser Aufzählung – mit
Ausnahme besonderer Methoden ("__str__()", "__add__()" usw.), von
Deskriptoren (Methoden sind auch Deskriptoren) und von Variablennamen,
die in "_ignore_" aufgeführt sind.

Beachte: Definiert deine Aufzählung "__new__()" und/oder "__init__()",
werden alle Werte, die dem Enum-Mitglied gegeben werden, an diese
Methoden übergeben. Ein Beispiel dazu ist Planet.

Bemerkung:

  Die Methode "__new__()" wird, falls definiert, beim Erzeugen der
  Enum-Mitglieder verwendet. Sie wird dann durch das "__new__()" von
  Enum ersetzt, das nach der Klassenerzeugung zum Nachschlagen
  bestehender Mitglieder dient. Näheres steht unter Wann __new__() und
  wann __init__().


Eingeschränkte Ableitung von Enum
=================================

Eine neue "Enum"-Klasse muss eine Basis-Enum-Klasse haben, höchstens
einen konkreten Datentyp und so viele auf "object" beruhende Mixin-
Klassen wie nötig. Die Reihenfolge dieser Basisklassen ist:

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

Außerdem ist es nur dann erlaubt, von einer Aufzählung abzuleiten,
wenn die Aufzählung keine Mitglieder definiert. Das hier ist also
verboten:

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

Das hier ist aber erlaubt:

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

Die Ableitung von Enums zuzulassen, die Mitglieder definieren, würde
wichtige Invarianten von Typen und Instanzen verletzen. Andererseits
ist es sinnvoll, gemeinsames Verhalten innerhalb einer Gruppe von
Aufzählungen zu teilen. (Ein Beispiel dazu ist OrderedEnum.)


Unterstützung für Dataclasses
=============================

Beim Erben von einer "dataclass" lässt "__repr__()" den Namen der
geerbten Klasse weg. 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>

Verwende das Argument "repr=False" von "dataclass()", um das übliche
"repr()" zu nutzen.

Geändert in Version 3.12: Im Wertebereich werden nur die Felder der
Dataclass gezeigt, nicht ihr Name.

Bemerkung:

  Den Decorator "@~dataclasses.dataclass" zu "Enum" und seinen
  Unterklassen hinzuzufügen wird nicht unterstützt. Es löst keine
  Fehler aus, führt zur Laufzeit aber zu sehr seltsamen Ergebnissen,
  etwa dazu, dass Mitglieder einander gleich 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
========

Aufzählungen lassen sich mit pickle serialisieren und wieder einlesen:

   >>> 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 pickle gelten: Enums, die sich
serialisieren lassen, müssen auf der obersten Ebene eines Moduls
definiert sein, weil sie zum Einlesen aus diesem Modul importierbar
sein müssen.

Bemerkung:

  Mit pickle-Protokollversion 4 lassen sich Enums, die in anderen
  Klassen stecken, mühelos serialisieren.

Es ist möglich, zu verändern, wie Enum-Mitglieder serialisiert und
eingelesen werden – indem man "__reduce_ex__()" in der
Aufzählungsklasse definiert. Standardmäßig geschieht das über den
Wert, aber Enums mit komplizierten Werten wollen möglicherweise über
den Namen gehen:

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

Bemerkung:

  Bei Flags ist der Weg über den Namen nicht empfehlenswert, weil
  unbenannte Aliase nicht wieder eingelesen werden.


Functional 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 "namedtuple". Das erste Argument des
Aufrufs von "Enum" ist der Name der Aufzählung.

Das zweite Argument ist die *Quelle* der Namen der
Aufzählungsmitglieder. Sie kann eine durch Leerraum getrennte
Zeichenkette von Namen sein, eine Folge von Namen, eine Folge von
2-Tupeln mit Schlüssel-Wert-Paaren oder eine Abbildung (etwa ein
Wörterbuch) von Namen auf Werte. Die letzten zwei Möglichkeiten
erlauben es, Aufzählungen beliebige Werte zuzuweisen. Die anderen
weisen automatisch aufsteigende Ganzzahlen ab 1 zu (verwende den
Parameter "start", um einen anderen Startwert anzugeben).
Zurückgegeben wird eine neue, von "Enum" abgeleitete Klasse. Anders
gesagt entspricht die obige Zuweisung an "Animal" Folgendem:

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

Der Grund, standardmäßig bei "1" und nicht bei "0" anzufangen, ist,
dass "0" im booleschen Sinne "False" ist, Enum-Mitglieder
standardmäßig aber alle zu "True" auswerten.

Enums, die mit der funktionalen API erzeugt wurden, zu serialisieren
kann heikel sein, weil Implementierungsdetails des Frame-Stapels
verwendet werden, um herauszufinden, in welchem Modul die Aufzählung
erzeugt wird (es scheitert zum Beispiel, wenn du eine Hilfsfunktion in
einem separaten Modul verwendest, und funktioniert möglicherweise auch
nicht unter IronPython oder Jython). Die Lösung ist, den Modulnamen
ausdrücklich anzugeben:

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

Warnung:

  Wird "module" nicht angegeben und kann Enum es nicht bestimmen,
  lassen sich die neuen Enum-Mitglieder nicht wieder einlesen, damit
  Fehler näher an ihrer Quelle bleiben, wird das Serialisieren
  abgeschaltet.

Das neue pickle-Protokoll 4 stützt sich unter bestimmten Umständen
außerdem darauf, dass "__qualname__" auf den Ort gesetzt ist, an dem
pickle die Klasse finden kann. Wurde die Klasse zum Beispiel in der
Klasse SomeData im globalen Gültigkeitsbereich verfügbar gemacht:

   >>> 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 festhält.

* *names*: Die Enum-Mitglieder. Das kann eine durch Leerraum oder
  Kommas getrennte Zeichenkette sein (die Werte beginnen bei 1, sofern
  nichts anderes angegeben ist):

     '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 Abbildung:

     {'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 eingemischt wird.

* *start*: Zahl, bei der zu zählen begonnen wird, wenn nur Namen
  übergeben werden.

Geändert in Version 3.5: Der Parameter *start* wurde ergänzt.


Abgeleitete Aufzählungen
========================


IntEnum
-------

Die erste angebotene Abwandlung von "Enum" ist zugleich eine
Unterklasse von "int". Mitglieder eines "IntEnum" lassen sich mit
Ganzzahlen vergleichen. In der Folge lassen sich ganzzahlige
Aufzählungen unterschiedlicher Typen auch miteinander vergleichen:

   >>> 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

Mit gewöhnlichen "Enum"-Aufzählungen lassen sie sich aber weiterhin
nicht vergleichen:

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

"IntEnum"-Werte verhalten sich auch auf andere Weisen wie Ganzzahlen,
wie du es erwartest:

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


StrEnum
-------

Die zweite angebotene Abwandlung von "Enum" ist zugleich eine
Unterklasse von "str". Mitglieder eines "StrEnum" lassen sich mit
Zeichenketten vergleichen. In der Folge lassen sich Zeichenketten-
Aufzählungen unterschiedlicher Typen auch miteinander vergleichen.

Added in version 3.11.


IntFlag
-------

Die nächste angebotene Abwandlung von "Enum", "IntFlag", beruht
ebenfalls auf "int". Der Unterschied ist, dass sich Mitglieder von
"IntFlag" mit den bitweisen Operatoren (&, |, ^, ~) verbinden lassen
und das Ergebnis, wenn möglich, weiterhin ein "IntFlag"-Mitglied ist.
Wie bei "IntEnum" sind "IntFlag"-Mitglieder auch Ganzzahlen und lassen
sich überall verwenden, wo ein "int" verwendet wird.

Bemerkung:

  Jede Operation auf einem "IntFlag"-Mitglied außer den bitweisen
  Operationen verliert die "IntFlag"-Zugehörigkeit.Bitweise
  Operationen, die zu ungültigen "IntFlag"-Werten führen, verlieren
  die "IntFlag"-Zugehörigkeit. Näheres steht bei "FlagBoundary".

Added in version 3.6.

Geändert in Version 3.11.

Beispiel einer "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>

Bemerkung:

  Benannte Kombinationen gelten als Aliase. Aliase erscheinen beim
  Iterieren nicht, können aber beim Nachschlagen über den Wert
  zurückgegeben werden.

Geändert in Version 3.11.

Ein weiterer wichtiger Unterschied zwischen "IntFlag" und "Enum" ist:
Ist kein Flag gesetzt (der Wert ist 0), ist die boolesche Auswertung
"False":

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

Weil "IntFlag"-Mitglieder auch Unterklassen von "int" sind, lassen sie
sich mit diesen verbinden (verlieren dabei aber möglicherweise die
"IntFlag"-Zugehörigkeit:

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

   >>> Perm.X + 8
   9

Bemerkung:

  Der Negationsoperator "~" gibt immer ein "IntFlag"-Mitglied mit
  positivem Wert zurück:

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

Über "IntFlag"-Mitglieder lässt sich außerdem iterieren:

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

Added in version 3.11.


Flag
----

Die letzte Abwandlung ist "Flag". Wie bei "IntFlag" lassen sich
"Flag"-Mitglieder mit den bitweisen Operatoren (&, |, ^, ~) verbinden.
Anders als bei "IntFlag" lassen sie sich weder mit einer anderen
"Flag"-Aufzählung noch mit "int" verbinden oder vergleichen. Die Werte
lassen sich zwar unmittelbar angeben, es ist aber empfehlenswert,
"auto" als Wert zu verwenden und "Flag" einen passenden Wert wählen zu
lassen.

Added in version 3.6.

Wie bei "IntFlag" ist die boolesche Auswertung "False", wenn eine
Kombination von "Flag"-Mitgliedern dazu führt, dass kein Flag gesetzt
ist:

   >>> 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, …), Kombinationen von Flags dagegen nicht:

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

Dem Zustand „kein Flag gesetzt“ einen Namen zu geben ändert seinen
booleschen Wert nicht:

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

Über "Flag"-Mitglieder lässt sich außerdem iterieren:

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

Added in version 3.11.

Bemerkung:

  Für den Großteil neuen Codes sind "Enum" und "Flag" sehr
  empfehlenswert, weil "IntEnum" und "IntFlag" einige semantische
  Versprechen einer Aufzählung brechen (indem sie mit Ganzzahlen und
  damit über die Transitivität mit anderen, nicht verwandten
  Aufzählungen vergleichbar sind). "IntEnum" und "IntFlag" sollten nur
  in Fällen verwendet werden, in denen "Enum" und "Flag" nicht genügen
  – zum Beispiel, wenn ganzzahlige Konstanten durch Aufzählungen
  ersetzt werden, oder für das Zusammenspiel mit anderen Systemen.


Weitere
-------

Zwar ist "IntEnum" Teil des Moduls "enum", es wäre aber sehr einfach,
es selbst umzusetzen:

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

Das zeigt, wie sich ähnliche abgeleitete Aufzählungen definieren
lassen – zum Beispiel ein "FloatEnum", das "float" anstelle von "int"
einmischt.

Einige Regeln:

1. Beim Ableiten von "Enum" müssen Mixin-Typen in der Folge der
   Basisklassen vor der "Enum"-Klasse selbst stehen, wie im
   "IntEnum"-Beispiel oben.

2. Mixin-Typen müssen ableitbar sein. So sind zum Beispiel "bool" und
   "range" nicht ableitbar und lösen beim Erzeugen des Enums einen
   Fehler aus, wenn sie als Mixin-Typ verwendet werden.

3. Ein "Enum" kann zwar Mitglieder jedes Typs haben, aber sobald du
   einen zusätzlichen Typ einmischst, müssen alle Mitglieder Werte
   dieses Typs haben, etwa "int" oben. Diese Einschränkung gilt nicht
   für Mixins, die nur Methoden ergänzen und keinen weiteren Typ
   angeben.

4. Wird ein anderer Datentyp eingemischt, ist das Attribut "value"
   *nicht dasselbe* wie das Enum-Mitglied selbst, auch wenn es
   gleichwertig ist und sich als gleich vergleicht.

5. Ein "data type" ist ein Mixin, das "__new__()" definiert, oder eine
   "dataclass"

6. Formatierung im %-Stil: "%s" und "%r" rufen "__str__()"
   beziehungsweise "__repr__()" der "Enum"-Klasse auf. Andere Codes
   (etwa "%i" oder "%h" bei IntEnum) behandeln das Enum-Mitglied als
   seinen eingemischten Typ.

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

Bemerkung:

  Weil "IntEnum", "IntFlag" und "StrEnum" als austauschbarer Ersatz
  für bestehende Konstanten entworfen sind, wurde ihre Methode
  "__str__()" auf die Methode "__str__()" ihres Datentyps
  zurückgesetzt.


Wann "__new__()" und wann "__init__()"
======================================

"__new__()" muss immer dann verwendet werden, wenn du den
tatsächlichen Wert des "Enum"-Mitglieds anpassen willst. Alle anderen
Änderungen können entweder in "__new__()" oder in "__init__()" stehen,
wobei "__init__()" zu bevorzugen ist.

Wenn du zum Beispiel mehrere Dinge an den Konstruktor übergeben
willst, 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:

  Rufe "super().__new__()" *nicht* auf, weil das nur zum Nachschlagen
  gedachte "__new__" gefunden wird. Verwende stattdessen den Datentyp
  unmittelbar.


Feinere Punkte
==============


Unterstützte "__dunder__"- und "_sunder_"-Namen
-----------------------------------------------

Die unterstützten "__dunder__"- und "_sunder_"-Namen stehen in der
API-Dokumentation zu Enum.


_Private__names
---------------

Private Namen werden nicht in Enum-Mitglieder umgewandelt, sondern
bleiben gewöhnliche Attribute.

Geändert in Version 3.11.


Der Typ von "Enum"-Mitgliedern
------------------------------

Enum-Mitglieder sind Instanzen ihrer Enum-Klasse und werden
normalerweise als "EnumClass.member" angesprochen. In bestimmten
Lagen, etwa beim Schreiben eigenen Enum-Verhaltens, ist es nützlich,
von einem Mitglied unmittelbar auf ein anderes zugreifen zu können,
und das wird unterstützt. Um Namenskollisionen zwischen Mitgliedsnamen
und Attributen beziehungsweise Methoden eingemischter Klassen zu
vermeiden, sind Großbuchstaben-Namen aber sehr empfehlenswert.

Geändert in Version 3.5.


Mitglieder erzeugen, die mit anderen Datentypen gemischt sind
-------------------------------------------------------------

Wenn du mit einem "Enum" von anderen Datentypen ableitest, etwa von
"int" oder "str", 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 Typen gemischt sind, die keine "Enum" sind (etwa
"int", "str" usw.), werden nach den Regeln des eingemischten Typs
ausgewertet. Andernfalls werten alle Mitglieder zu "True" aus. Damit
die boolesche Auswertung deines eigenen Enums vom Wert des Mitglieds
abhängt, ergänze in deiner Klasse Folgendes:

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

Einfache "Enum"-Klassen werten immer zu "True" aus.


"Enum"-Klassen mit Methoden
---------------------------

Wenn du deiner Enum-Unterklasse zusätzliche Methoden gibst, wie der
Klasse Planet unten, erscheinen diese Methoden in einem "dir()" des
Mitglieds, aber nicht in dem 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']


Mitglieder von "Flag" verbinden
-------------------------------

Beim Iterieren über eine Kombination von "Flag"-Mitgliedern kommen nur
die Mitglieder zurück, 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>


Kleinigkeiten zu "Flag" und "IntFlag"
-------------------------------------

Wir verwenden für unsere Beispiele den folgenden Ausschnitt:

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

Dann gilt Folgendes:

* Flags mit einem einzelnen Bit sind kanonisch

* Flags mit mehreren Bits und mit null Bits sind Aliase

* beim Iterieren werden nur kanonische Flags zurückgegeben:

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

* ein Flag oder eine Flag-Menge zu negieren gibt ein neues Flag
  beziehungsweise eine neue Flag-Menge mit dem entsprechenden
  positiven Ganzzahlwert zurück:

     >>> Color.BLUE
     <Color.BLUE: 4>

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

* Namen von Pseudo-Flags werden aus den Namen ihrer Mitglieder
  gebildet:

     >>> (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

* Flags mit mehreren Bits, also Aliase, können aus Operationen
  zurückkommen:

     >>> Color.RED | Color.BLUE
     <Color.PURPLE: 5>

     >>> Color(7)  # or Color(-1)
     <Color.WHITE: 7>

     >>> Color(0)
     <Color.BLACK: 0>

* Prüfung auf Zugehörigkeit beziehungsweise Enthaltensein: Flags mit
  dem Wert null gelten immer als enthalten:

     >>> Color.BLACK in Color.WHITE
     True

  andernfalls wird True nur dann zurückgegeben, wenn alle Bits des
  einen Flags im anderen Flag enthalten sind:

     >>> Color.PURPLE in Color.WHITE
     True

     >>> Color.GREEN in Color.PURPLE
     False

Es gibt einen neuen Grenzmechanismus, der steuert, wie Bits außerhalb
des Bereichs beziehungsweise ungültige Bits behandelt werden:
"STRICT", "CONFORM", "EJECT" und "KEEP":

* STRICT --> löst bei ungültigen Werten eine Ausnahme aus

* CONFORM --> verwirft alle ungültigen Bits

* EJECT --> verliert den Flag-Status und wird zu einem gewöhnlichen
  int mit dem gegebenen Wert

* KEEP --> behält die zusätzlichen Bits

  * behält den Flag-Status und die zusätzlichen Bits

  * zusätzliche Bits erscheinen beim Iterieren nicht

  * zusätzliche Bits erscheinen in repr() und str()

Die Vorgabe für Flag ist "STRICT", die Vorgabe für "IntFlag" ist
"EJECT", und die Vorgabe für "_convert_" ist "KEEP" (ein Beispiel
dafür, wann "KEEP" gebraucht wird, ist "ssl.Options").


Wie unterscheiden sich Enums und Flags?
=======================================

Enums haben eine eigene Metaklasse, die viele Aspekte sowohl der
abgeleiteten "Enum"-Klassen als auch ihrer Instanzen (Mitglieder)
beeinflusst.


Enum-Klassen
------------

Die Metaklasse "EnumType" ist dafür zuständig, "__contains__()",
"__dir__()", "__iter__()" und weitere Methoden bereitzustellen, mit
denen man mit einer "Enum"-Klasse Dinge tun kann, die bei einer
gewöhnlichen Klasse scheitern, etwa "list(Color)" oder "some_enum_var
in Color". "EnumType" ist dafür zuständig, sicherzustellen, dass
verschiedene andere Methoden der endgültigen "Enum"-Klasse korrekt
sind (etwa "__new__()", "__getnewargs__()", "__str__()" und
"__repr__()").


Flag-Klassen
------------

Flags haben eine erweiterte Sicht auf Aliase: Um kanonisch zu sein,
muss der Wert eines Flags eine Potenz von zwei sein und darf kein
doppelter Name sein. Zusätzlich zur "Enum"-Begriffsbestimmung eines
Alias gilt daher ein Flag ohne Wert (also "0") oder mit mehr als einem
Wert, der eine Potenz von zwei ist (etwa "3"), als Alias.


Enum-Mitglieder (also Instanzen)
--------------------------------

Das interessanteste an Enum-Mitgliedern ist, dass sie Singletons sind.
"EnumType" erzeugt sie alle, während es die Enum-Klasse selbst
erzeugt, und setzt dann ein eigenes "__new__()" ein, das sicherstellt,
dass nie neue erzeugt werden – indem es nur die bestehenden
Mitgliedsinstanzen zurückgibt.


Flag-Mitglieder
---------------

Über Flag-Mitglieder lässt sich genauso iterieren wie über die
"Flag"-Klasse, und es kommen nur die kanonischen Mitglieder zurück.
Zum Beispiel:

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

(Beachte, dass "BLACK", "PURPLE" und "WHITE" nicht erscheinen.)

Ein Flag-Mitglied zu invertieren gibt den entsprechenden positiven
Wert zurück und nicht einen negativen Wert – zum Beispiel:

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

Flag-Mitglieder haben eine Länge, die der Anzahl der Werte entspricht,
die sie enthalten und die Potenzen von zwei sind. Zum Beispiel:

   >>> len(Color.PURPLE)
   2


Enum-Kochbuch
=============

"Enum", "IntEnum", "StrEnum", "Flag" und "IntFlag" dürften den
Großteil der Anwendungsfälle abdecken, aber nicht alle. Hier Rezepte
für einige verschiedene Arten von Aufzählungen, die sich unmittelbar
verwenden lassen oder als Beispiele dienen, um eigene zu bauen.


Werte weglassen
---------------

In vielen Anwendungsfällen ist es einem gleichgültig, welchen Wert
eine Aufzählung tatsächlich hat. Es gibt mehrere Wege, diese Art
einfacher Aufzählung zu definieren:

* Instanzen von "auto" als Wert verwenden

* Instanzen von "object" als Wert verwenden

* eine beschreibende Zeichenkette als Wert verwenden

* ein Tupel als Wert und ein eigenes "__new__()" verwenden, um das
  Tupel durch einen "int"-Wert zu ersetzen

Jeden dieser Wege zu verwenden zeigt dem Benutzer, dass diese Werte
nicht wichtig sind, und erlaubt es außerdem, Mitglieder hinzuzufügen,
zu entfernen oder umzuordnen, ohne die übrigen Mitglieder neu zu
nummerieren.


"auto" verwenden
~~~~~~~~~~~~~~~~

"auto" zu verwenden sähe so aus:

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


"object" verwenden
~~~~~~~~~~~~~~~~~~

"object" zu verwenden sähe so aus:

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

Das ist auch ein gutes Beispiel dafür, warum du vielleicht dein
eigenes "__repr__()" schreiben willst:

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


Eine beschreibende Zeichenkette verwenden
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Eine Zeichenkette als Wert zu verwenden sähe so aus:

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


Ein eigenes "__new__()" verwenden
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Ein automatisch nummerierendes "__new__()" zu verwenden sähe so aus:

   >>> 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 allgemeiner verwendbares "AutoNumber" zu bauen, ergänze "*args"
in der Signatur:

   >>> 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 du dann von "AutoNumber" erbst, kannst du dein eigenes "__init__"
schreiben, um zusätzliche Argumente zu behandeln:

   >>> 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'

Bemerkung:

  Die Methode "__new__()" wird, falls definiert, beim Erzeugen der
  Enum-Mitglieder verwendet. Sie wird dann durch das "__new__()" von
  Enum ersetzt, das nach der Klassenerzeugung zum Nachschlagen
  bestehender Mitglieder dient.

Warnung:

  Rufe "super().__new__()" *nicht* auf, weil das nur zum Nachschlagen
  gedachte "__new__" gefunden wird. Verwende stattdessen den Datentyp
  unmittelbar – etwa so:

     obj = int.__new__(cls, value)


OrderedEnum
-----------

Eine geordnete Aufzählung, die nicht auf "IntEnum" beruht und daher
die üblichen "Enum"-Invarianten erhält (etwa, nicht mit anderen
Aufzählungen vergleichbar zu sein):

   >>> 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 doppelter Mitgliedswert gefunden wird,
anstatt einen Alias zu erzeugen:

   >>> 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'

Bemerkung:

  Das ist ein nützliches Beispiel dafür, von Enum abzuleiten, um
  anderes Verhalten zu ergänzen oder zu ändern und zugleich Aliase zu
  verbieten. Soll nur das Verbieten von Aliasen geändert werden, lässt
  sich stattdessen der Decorator "unique()" verwenden.


MultiValueEnum
--------------

Unterstützt mehr als einen Wert je 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
------

Ist "__new__()" oder "__init__()" definiert, 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, das das Attribut "_ignore_" im Einsatz zeigt:

   >>> import datetime as dt
   >>> class Period(dt.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)>]


Von EnumType ableiten
=====================

Die meisten Bedürfnisse rund um Enums lassen sich dadurch erfüllen,
dass man "Enum"-Unterklassen anpasst – entweder mit Klassen-
Decoratoren oder mit eigenen Funktionen. Es ist aber möglich, von
"EnumType" abzuleiten, um ein anderes Enum-Erlebnis zu bieten.
