Code: Alles auswählen
from collections import defaultdict
from itertools import count
class MetaEnum(type):
@classmethod
def __prepare__(metacls, name, bases):
return defaultdict(count().__next__)
def __new__(cls, name, bases, cdict):
return type.__new__(cls, name, bases, dict(cdict))
class Enum(metaclass=MetaEnum):
pass
Code: Alles auswählen
class MyEnum(Enum):
foo
bar
baz
zig = 5
zag = baz
print(MyEnum.foo)
print(MyEnum.bar)
print(MyEnum.baz)
print(MyEnum.zig)
print(MyEnum.zag)
Code: Alles auswählen
$ python enum.py
1
2
3
5
3
Wenn Python eine Klassendefinition ausführt, nimmt es als Namensraum für die Klasse das, was von der __prepare__()-Methode ihrer Metaklasse zurückgegeben wird. Das kann im Prinzip jede Art von Dictionary sein, in meinem Fall ist es ein defaultdict mit der Factory Funktion count().__next__, so dass für jeden nicht gefundenen Namen dieser angelegt und mit der nächsten Zahl assoziiert wird. Die erste Zahl des Enums ist deswegen 1 und nicht 0, weil als erstes anscheinend das __name__-Attribut im Namensraum gesucht wird, was den ersten Aufruf von count().__next__() triggert. Das Attribut wird allerdings später wieder irgendwo mit dem Namen der Klasse überschrieben.
Wichtig: In __new__() sollte man dieses Dictionary wieder durch ein normales dict ersetzen, sonst passieren komische Sachen.