Frage Wie kann ich ein 'Enum' in Python darstellen?


Ich bin hauptsächlich ein C # -Entwickler, aber ich arbeite gerade an einem Projekt in Python.

Wie kann ich das Äquivalent eines Enums in Python darstellen?


1146


Ursprung


Antworten:


Enums wurden zu Python 3.4 hinzugefügt, wie in beschrieben PEP 435. Es war auch zurück zu 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 und 2.4 auf Pypi.

Für fortgeschrittene Enum-Techniken versuchen Sie die Aenum-Bibliothek (2.7, 3.3+, derselbe Autor wie enum34. Der Code ist zwischen py2 und py3, z.B. Du brauchst __order__ in Python 2).

  • Benutzen enum34, machen $ pip install enum34
  • Benutzen aenum, machen $ pip install aenum

Installieren enum (keine Nummern) wird eine komplett andere und inkompatible Version installieren.


from enum import Enum     # for enum34, or the stdlib version
# from aenum import Enum  # for the aenum version
Animal = Enum('Animal', 'ant bee cat dog')

Animal.ant  # returns <Animal.ant: 1>
Animal['ant']  # returns <Animal.ant: 1> (string lookup)
Animal.ant.name  # returns 'ant' (inverse lookup)

oder gleichwertig:

class Animal(Enum):
    ant = 1
    bee = 2
    cat = 3
    dog = 4

In früheren Versionen war eine Möglichkeit, Enums zu erreichen:

def enum(**enums):
    return type('Enum', (), enums)

das wird so verwendet:

>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'

Sie können die automatische Aufzählung auch leicht mit etwas wie diesem unterstützen:

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    return type('Enum', (), enums)

und so benutzt:

>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ZERO
0
>>> Numbers.ONE
1

Unterstützung für das Zurückkonvertieren der Werte in Namen kann folgendermaßen hinzugefügt werden:

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    reverse = dict((value, key) for key, value in enums.iteritems())
    enums['reverse_mapping'] = reverse
    return type('Enum', (), enums)

Dies überschreibt alles mit diesem Namen, aber es ist nützlich für das Rendern Ihrer Enums in der Ausgabe. Wenn die umgekehrte Zuordnung nicht vorhanden ist, wird KeyError ausgelöst. Mit dem ersten Beispiel:

>>> Numbers.reverse_mapping['three']
'THREE'

2320



Vor PEP 435 hatte Python keine Entsprechung, aber Sie konnten Ihre eigenen implementieren.

Ich mag es, es einfach zu halten (ich habe einige schrecklich komplexe Beispiele im Netz gesehen), so etwas ...

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

In Python 3.4 (PEP 435), du kannst das schaffen Enum die Basisklasse. Dies bringt Ihnen ein wenig zusätzliche Funktionalität, die im PEP beschrieben wird. Enum-Mitglieder unterscheiden sich beispielsweise von ganzen Zahlen und bestehen aus a name und ein value.

class Animal(Enum):
    DOG = 1
    CAT = 2

print(Animal.DOG)
# <Animal.DOG: 1>

print(Animal.DOG.value)
# 1

print(Animal.DOG.name)
# "DOG"

Wenn Sie die Werte nicht eingeben möchten, verwenden Sie die folgende Verknüpfung:

class Animal(Enum):
    DOG, CAT = range(2)

Enum Implementierungen kann in Listen umgewandelt werden und ist iterierbar. Die Reihenfolge ihrer Mitglieder ist die Deklarationsreihenfolge und hat nichts mit ihren Werten zu tun. Beispielsweise:

class Animal(Enum):
    DOG = 1
    CAT = 2
    COW = 0

list(Animal)
# [<Animal.DOG: 1>, <Animal.CAT: 2>, <Animal.COW: 0>]

[animal.value for animal in Animal]
# [1, 2, 0]

Animal.CAT in Animal
# True

721



Hier ist eine Implementierung:

class Enum(set):
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

Hier ist seine Verwendung:

Animals = Enum(["DOG", "CAT", "HORSE"])

print(Animals.DOG)

298



Wenn Sie die numerischen Werte benötigen, hier ist der schnellste Weg:

dog, cat, rabbit = range(3)

In Python 3.x können Sie am Ende auch einen Platzhalter hinzufügen, der alle verbleibenden Werte des Bereichs aufnimmt, für den Fall, dass es Ihnen nichts ausmacht, Speicher zu verschwenden und nicht zählen kann:

dog, cat, rabbit, horse, *_ = range(100)

183



Die beste Lösung für Sie hängt davon ab, was Sie von Ihrem benötigen Fälschung  enum.

Einfache enum:

Wenn du das brauchst enum als nur eine Liste von Namen anders identifizieren Artikel, die Lösung von Mark Harrison (oben) ist großartig:

Pen, Pencil, Eraser = range(0, 3)

Verwendung einer range erlaubt Ihnen auch, irgendwelche einzustellen Startwert:

Pen, Pencil, Eraser = range(9, 12)

Zusätzlich zu den oben genannten, wenn Sie auch benötigen, dass die Elemente zu einem gehören Container von einer Art, dann bette sie in eine Klasse ein:

class Stationery:
    Pen, Pencil, Eraser = range(0, 3)

Um das Enumerationselement zu verwenden, müssen Sie nun den Containernamen und den Elementnamen verwenden:

stype = Stationery.Pen

Komplexes Enum:

Für lange Listen von Enum- oder komplizierteren Verwendungen von Enum werden diese Lösungen nicht ausreichen. Sie können nach dem Rezept von Will Ware für suchen Simulieren von Aufzählungen in Python veröffentlicht in der Python Kochbuch. Eine Online-Version davon ist verfügbar Hier.

Mehr Info:

PEP 354: Aufzählungen in Python hat die interessanten Details eines Vorschlags für enum in Python und wieso es abgelehnt wurde.


119



Das typsichere enum-Muster, das in Java vor JDK 5 verwendet wurde, hat a Anzahl der Vorteile. Ähnlich wie in Alexandrus Antwort erstellen Sie ein Klassen- und Klassenebenenfelder sind die Enum-Werte; jedoch das enum Werte sind Instanzen der Klasse und nicht kleine ganze Zahlen. Das hat Der Vorteil, dass Ihre Enum-Werte nicht versehentlich gleichwertig sind zu kleinen ganzen Zahlen können Sie steuern, wie sie gedruckt werden, fügen Sie beliebig hinzu Methoden, wenn das nützlich ist und Assertions mit isinstance machen:

class Animal:
   def __init__(self, name):
       self.name = name

   def __str__(self):
       return self.name

   def __repr__(self):
       return "<Animal: %s>" % self

Animal.DOG = Animal("dog")
Animal.CAT = Animal("cat")

>>> x = Animal.DOG
>>> x
<Animal: dog>
>>> x == 1
False

Ein kürzlich thread auf python-dev darauf hingewiesen, gibt es ein paar Enum-Bibliotheken in der Wildnis, einschließlich:


76



Eine Enum-Klasse kann ein Einzeiler sein.

class Enum(tuple): __getattr__ = tuple.index

Wie man es benutzt (Vorwärts- und Rückwärtssuche, Schlüssel, Werte, Artikel, etc.)

>>> State = Enum(['Unclaimed', 'Claimed'])
>>> State.Claimed
1
>>> State[1]
'Claimed'
>>> State
('Unclaimed', 'Claimed')
>>> range(len(State))
[0, 1]
>>> [(k, State[k]) for k in range(len(State))]
[(0, 'Unclaimed'), (1, 'Claimed')]
>>> [(k, getattr(State, k)) for k in State]
[('Unclaimed', 0), ('Claimed', 1)]

56



Python hat kein eingebautes Äquivalent zu enumund andere Antworten haben Ideen für die Umsetzung Ihrer eigenen (Sie könnten auch interessiert sein an der über die Top-Version im Python Kochbuch).

In Situationen jedoch, in denen enum wäre in C gefragt, ich lande normalerweise nur mit einfachen Strings: Wegen der Art, wie Objekte / Attribute implementiert werden, (C) Python ist optimiert, um sehr schnell mit kurzen Strings zu arbeiten, so dass es keinen wirklichen Leistungsvorteil für die Verwendung von ganzen Zahlen geben würde. Um sich vor Tippfehlern und ungültigen Werten zu schützen, können Sie Checks an ausgewählten Stellen einfügen.

ANIMALS = ['cat', 'dog', 'python']

def take_for_a_walk(animal):
    assert animal in ANIMALS
    ...

(Ein Nachteil gegenüber der Verwendung einer Klasse ist, dass Sie den Vorteil der automatischen Vervollständigung verlieren)


44



Also stimme ich zu. Lassen Sie uns die Typsicherheit in Python nicht erzwingen, aber ich möchte mich vor dummen Fehlern schützen. Also, was denken wir darüber?

class Animal(object):
    values = ['Horse','Dog','Cat']

    class __metaclass__(type):
        def __getattr__(self, name):
            return self.values.index(name)

Es hält mich von Wert-Kollision bei der Definition meiner Enums.

>>> Animal.Cat
2

Es gibt noch einen weiteren Vorteil: sehr schnelle Reverse-Lookups:

def name_of(self, i):
    return self.values[i]

44