Frage

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

Wie kann ich das Äquivalent einer Enum in Python darstellen?

War es hilfreich?

Lösung

Aufzählungen wurden zu Python 3.4 hinzugefügt, wie in beschrieben PEP 435.Das ist auch so gewesen zurückportiert auf 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 und 2.4 auf Pypi.

Für fortgeschrittenere Enum-Techniken versuchen Sie es mit Aenum-Bibliothek (2.7, 3.3+, gleicher Autor wie enum34.Der Code ist zwischen py2 und py3 nicht perfekt kompatibel, z. B.Du brauchst __order__ in Python 2).

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

Installieren enum (keine Zahlen) installiert eine völlig andere und inkompatible Version.


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 gab es eine Möglichkeit, Aufzählungen durchzuführen:

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

was so verwendet wird:

>>> 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 ganz einfach mit etwas wie diesem unterstützen:

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

und so verwendet:

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

Unterstützung für die Rückkonvertierung der Werte in Namen kann auf diese Weise 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)

Dadurch wird alles mit diesem Namen überschrieben, es ist jedoch nützlich, um Ihre Aufzählungen in der Ausgabe darzustellen.Es wird KeyError ausgelöst, wenn die umgekehrte Zuordnung nicht vorhanden ist.Mit dem ersten Beispiel:

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

Andere Tipps

Vor PEP 435 gab es für Python kein Äquivalent, aber Sie konnten Ihr eigenes implementieren.

Ich persönlich mag es, es einfach zu halten (ich habe einige schrecklich komplexe Beispiele im Internet gesehen), etwa so ...

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

In Python 3.4 (PEP 435), du kannst das schaffen Aufzählung die Basisklasse.Dadurch erhalten Sie ein wenig zusätzliche Funktionalität, die im PEP beschrieben wird.Enum-Mitglieder unterscheiden sich beispielsweise von Ganzzahlen 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 können in Listen umgewandelt werden und sind iterierbar.Die Reihenfolge seiner Mitglieder ist die Deklarationsreihenfolge und hat nichts mit ihren Werten zu tun.Zum Beispiel:

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

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)

Wenn Sie die numerischen Werte benötigen, gehen Sie hier am schnellsten vor:

dog, cat, rabbit = range(3)

In Python 3.x können Sie am Ende auch einen markierten Platzhalter hinzufügen, der alle verbleibenden Werte des Bereichs aufnimmt, falls Sie nichts dagegen haben, Speicher zu verschwenden und nicht zählen können:

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

Die beste Lösung für Sie hängt davon ab, was Sie von Ihrem Unternehmen erwarten gefälscht enum.

Einfache Aufzählung:

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

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

Verwendung einer range Außerdem können Sie beliebige festlegen Startwert:

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

Wenn Sie außerdem verlangen, dass die Artikel zu einem gehören Container von irgendeiner Art, dann betten Sie sie in eine Klasse ein:

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

Um das Enum-Element zu verwenden, müssen Sie nun den Containernamen und den Elementnamen verwenden:

stype = Stationery.Pen

Komplexe Aufzählung:

Für lange Aufzählungslisten oder kompliziertere Verwendungen von Aufzählungen reichen diese Lösungen nicht aus.Sie können sich das Rezept von Will Ware ansehen Aufzählungen in Python simulieren veröffentlicht in der Python-Kochbuch.Eine Online-Version davon ist verfügbar Hier.

Mehr Info:

PEP 354:Aufzählungen in Python enthält die interessanten Details eines Vorschlags für eine Aufzählung in Python und warum er abgelehnt wurde.

Das in Java Pre-Jdk 5 verwendete TypeSafe Enum-Muster hat eine Reihe von Vorteilen.Ähnlich wie in Alexandrus Antwort erstellen Sie eine Klassen- und Klassenebene, die die Enum -Werte sind.Die Enum -Werte sind jedoch eher Fälle der Klasse als in Kleinzahlen.Dies hat den Vorteil, dass Ihre Enum -Werte nicht versehentlich gleich mit kleinen Ganzzahlen verglichen werden.

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 wies darauf hin, dass es ein paar Enum-Bibliotheken gibt, darunter:

Eine Enum-Klasse kann einzeilig sein.

class Enum(tuple): __getattr__ = tuple.index

Verwendung (Vorwärts- und Rückwärtssuche, Schlüssel, Werte, Elemente usw.)

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

Python verfügt über kein integriertes Äquivalent zu enum, und andere Antworten enthalten Ideen für die Implementierung Ihrer eigenen (vielleicht sind Sie auch daran interessiert). Over-the-Top-Version im Python-Kochbuch).

In Situationen, in denen ein enum in C erforderlich wäre, lande ich normalerweise Ich verwende nur einfache Zeichenfolgen:Aufgrund der Art und Weise, wie Objekte/Attribute implementiert werden, ist (C)Python ohnehin darauf optimiert, mit kurzen Zeichenfolgen sehr schnell zu arbeiten, sodass die Verwendung von Ganzzahlen keinen wirklichen Leistungsvorteil mit sich bringt.Um Tippfehlern und ungültigen Werten vorzubeugen, können Sie an ausgewählten Stellen Häkchen einfügen.

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

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

(Ein Nachteil im Vergleich zur Verwendung einer Klasse besteht darin, dass Sie den Vorteil der automatischen Vervollständigung verlieren.)

Also, ich stimme zu.Lassen Sie uns in Python keine Typensicherheit erzwingen, aber ich möchte mich vor dummen Fehlern schützen.Was denken wir also darüber?

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

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

Es bewahrt mich vor Wertkollisionen bei der Definition meiner Aufzählungen.

>>> Animal.Cat
2

Es gibt noch einen weiteren praktischen Vorteil:wirklich schnelle Reverse-Lookups:

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

Am 10.05.2013 stimmte Guido der Annahme zu PEP 435 in die Python 3.4-Standardbibliothek.Das bedeutet, dass Python endlich über eine integrierte Unterstützung für Aufzählungen verfügt!

Für Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 und 2.4 ist ein Backport verfügbar.Es ist auf Pypi als enum34.

Erklärung:

>>> from enum import Enum
>>> class Color(Enum):
...     red = 1
...     green = 2
...     blue = 3

Darstellung:

>>> print(Color.red)
Color.red
>>> print(repr(Color.red))
<Color.red: 1>

Wiederholung:

>>> for color in Color:
...   print(color)
...
Color.red
Color.green
Color.blue

Programmgesteuerter Zugriff:

>>> Color(1)
Color.red
>>> Color['blue']
Color.blue

Weitere Informationen finden Sie unter der Antrag.Die offizielle Dokumentation wird voraussichtlich bald folgen.

Ich bevorzuge es, Aufzählungen in Python wie folgt zu definieren:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

Es ist fehlersicherer als die Verwendung von Ganzzahlen, da Sie sich nicht darum kümmern müssen, dass die Ganzzahlen eindeutig sind (z. B.Wenn Sie Hund = 1 und Katze = 1 sagen würden, wären Sie am Arsch.

Es ist fehlersicherer als die Verwendung von Zeichenfolgen, da Sie sich keine Sorgen über Tippfehler machen müssen (z. B.x == „catt“ schlägt stillschweigend fehl, aber x == Animal.Catt ist eine Laufzeitausnahme).

def M_add_class_attribs(attribs):
    def foo(name, bases, dict_):
        for v, k in attribs:
            dict_[k] = v
        return type(name, bases, dict_)
    return foo

def enum(*names):
    class Foo(object):
        __metaclass__ = M_add_class_attribs(enumerate(names))
        def __setattr__(self, name, value):  # this makes it read-only
            raise NotImplementedError
    return Foo()

Benutze es so:

Animal = enum('DOG', 'CAT')
Animal.DOG # returns 0
Animal.CAT # returns 1
Animal.DOG = 2 # raises NotImplementedError

Wenn Sie nur eindeutige Symbole möchten und sich nicht um die Werte kümmern, ersetzen Sie diese Zeile:

__metaclass__ = M_add_class_attribs(enumerate(names))

mit diesem:

__metaclass__ = M_add_class_attribs((object(), name) for name in names)

Hmmm...Ich nehme an, das, was einer Aufzählung am nächsten kommt, wäre ein Wörterbuch, das entweder wie folgt definiert ist:

months = {
    'January': 1,
    'February': 2,
    ...
}

oder

months = dict(
    January=1,
    February=2,
    ...
)

Dann können Sie den symbolischen Namen für die Konstanten wie folgt verwenden:

mymonth = months['January']

Es gibt andere Optionen, wie eine Liste von Tupeln oder ein Tupel von Tupeln, aber das Wörterbuch ist das einzige, das Ihnen eine "symbolische" (konstante Zeichenfolge) Möglichkeit bietet, auf den Wert zuzugreifen.

Bearbeiten:Mir gefällt auch Alexandrus Antwort!

Eine weitere, sehr einfache Implementierung einer Aufzählung in Python, mit namedtuple:

from collections import namedtuple

def enum(*keys):
    return namedtuple('Enum', keys)(*keys)

MyEnum = enum('FOO', 'BAR', 'BAZ')

oder alternativ,

# With sequential number values
def enum(*keys):
    return namedtuple('Enum', keys)(*range(len(keys)))

# From a dict / keyword args
def enum(**kwargs):
    return namedtuple('Enum', kwargs.keys())(*kwargs.values())

Wie die obige Methode, die Unterklassen erstellt set, Dies erlaubt:

'FOO' in MyEnum
other = MyEnum.FOO
assert other == MyEnum.FOO

Bietet aber mehr Flexibilität, da es unterschiedliche Schlüssel und Werte haben kann.Dies erlaubt

MyEnum.FOO < MyEnum.BAR

verhält sich wie erwartet, wenn Sie die Version verwenden, die fortlaufende Zahlenwerte ausfüllt.

Was ich verwende:

class Enum(object):
    def __init__(self, names, separator=None):
        self.names = names.split(separator)
        for value, name in enumerate(self.names):
            setattr(self, name.upper(), value)
    def tuples(self):
        return tuple(enumerate(self.names))

Wie benutzt man:

>>> state = Enum('draft published retracted')
>>> state.DRAFT
0
>>> state.RETRACTED
2
>>> state.FOO
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
AttributeError: 'Enum' object has no attribute 'FOO'
>>> state.tuples()
((0, 'draft'), (1, 'published'), (2, 'retracted'))

Dadurch erhalten Sie ganzzahlige Konstanten wie state.PUBLISHED und die beiden Tupel, die Sie als Auswahlmöglichkeiten in Django-Modellen verwenden können.

Ab Python 3.4 wird es offizielle Unterstützung für Enums geben.Hier finden Sie Dokumentation und Beispiele hier auf der Python 3.4-Dokumentationsseite.

Aufzählungen werden mit der Klassensyntax erstellt, wodurch sie leicht zu lesen und zu schreiben können.Eine alternative Erstellungsmethode ist in funktionaler API beschrieben.Um eine Aufzählung zu definieren, unterordnen Sie Enum wie folgt:

from enum import Enum
class Color(Enum):
     red = 1
     green = 2
     blue = 3

Davidg empfiehlt die Verwendung von Diktaten.Ich würde noch einen Schritt weiter gehen und Sets verwenden:

months = set('January', 'February', ..., 'December')

Jetzt können Sie wie folgt testen, ob ein Wert mit einem der Werte in der Menge übereinstimmt:

if m in months:

Wie bei dF verwende ich jedoch normalerweise nur String-Konstanten anstelle von Aufzählungen.

Das ist das Beste, was ich je gesehen habe:„Erstklassige Aufzählungen in Python“

http://code.activestate.com/recipes/413486/

Es gibt Ihnen eine Klasse, und die Klasse enthält alle Aufzählungen.Die Aufzählungen können miteinander verglichen werden, haben aber keinen besonderen Wert;Sie können sie nicht als ganzzahligen Wert verwenden.(Ich habe mich zunächst dagegen gewehrt, weil ich an C-Aufzählungen gewöhnt bin, bei denen es sich um ganzzahlige Werte handelt.Aber wenn Sie es nicht als Ganzzahl verwenden können, können Sie es nicht versehentlich als Ganzzahl verwenden. Insgesamt denke ich also, dass es ein Gewinn ist.) Jede Aufzählung ist ein eindeutiger Wert.Sie können Aufzählungen drucken, Sie können sie durchlaufen und testen, ob ein Aufzählungswert „in“ der Aufzählung ist.Es ist ziemlich vollständig und glatt.

Bearbeiten (cfi):Der obige Link ist nicht mit Python 3 kompatibel.Hier ist meine Portierung von enum.py auf Python 3:

def cmp(a,b):
   if a < b: return -1
   if b < a: return 1
   return 0


def Enum(*names):
   ##assert names, "Empty enums are not supported" # <- Don't like empty enums? Uncomment!

   class EnumClass(object):
      __slots__ = names
      def __iter__(self):        return iter(constants)
      def __len__(self):         return len(constants)
      def __getitem__(self, i):  return constants[i]
      def __repr__(self):        return 'Enum' + str(names)
      def __str__(self):         return 'enum ' + str(constants)

   class EnumValue(object):
      __slots__ = ('__value')
      def __init__(self, value): self.__value = value
      Value = property(lambda self: self.__value)
      EnumType = property(lambda self: EnumType)
      def __hash__(self):        return hash(self.__value)
      def __cmp__(self, other):
         # C fans might want to remove the following assertion
         # to make all enums comparable by ordinal value {;))
         assert self.EnumType is other.EnumType, "Only values from the same enum are comparable"
         return cmp(self.__value, other.__value)
      def __lt__(self, other):   return self.__cmp__(other) < 0
      def __eq__(self, other):   return self.__cmp__(other) == 0
      def __invert__(self):      return constants[maximum - self.__value]
      def __nonzero__(self):     return bool(self.__value)
      def __repr__(self):        return str(names[self.__value])

   maximum = len(names) - 1
   constants = [None] * len(names)
   for i, each in enumerate(names):
      val = EnumValue(i)
      setattr(EnumClass, each, val)
      constants[i] = val
   constants = tuple(constants)
   EnumType = EnumClass()
   return EnumType


if __name__ == '__main__':
   print( '\n*** Enum Demo ***')
   print( '--- Days of week ---')
   Days = Enum('Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su')
   print( Days)
   print( Days.Mo)
   print( Days.Fr)
   print( Days.Mo < Days.Fr)
   print( list(Days))
   for each in Days:
      print( 'Day:', each)
   print( '--- Yes/No ---')
   Confirmation = Enum('No', 'Yes')
   answer = Confirmation.No
   print( 'Your answer is not', ~answer)

Halte es einfach:

class Enum(object): 
    def __init__(self, tupleList):
            self.tupleList = tupleList

    def __getattr__(self, name):
            return self.tupleList.index(name)

Dann:

DIRECTION = Enum(('UP', 'DOWN', 'LEFT', 'RIGHT'))
DIRECTION.DOWN
1

Ich hatte Gelegenheit, eine Enum-Klasse zu benötigen, um ein Binärdateiformat zu dekodieren.Die Funktionen, die ich zufällig wollte, sind eine prägnante Enumerationsdefinition, die Möglichkeit, Instanzen der Enumeration entweder nach Ganzzahlwert oder Zeichenfolge frei zu erstellen, und eine nützliche Funktion reprPräsentation.Hier ist, was ich am Ende herausgefunden habe:

>>> class Enum(int):
...     def __new__(cls, value):
...         if isinstance(value, str):
...             return getattr(cls, value)
...         elif isinstance(value, int):
...             return cls.__index[value]
...     def __str__(self): return self.__name
...     def __repr__(self): return "%s.%s" % (type(self).__name__, self.__name)
...     class __metaclass__(type):
...         def __new__(mcls, name, bases, attrs):
...             attrs['__slots__'] = ['_Enum__name']
...             cls = type.__new__(mcls, name, bases, attrs)
...             cls._Enum__index = _index = {}
...             for base in reversed(bases):
...                 if hasattr(base, '_Enum__index'):
...                     _index.update(base._Enum__index)
...             # create all of the instances of the new class
...             for attr in attrs.keys():
...                 value = attrs[attr]
...                 if isinstance(value, int):
...                     evalue = int.__new__(cls, value)
...                     evalue._Enum__name = attr
...                     _index[value] = evalue
...                     setattr(cls, attr, evalue)
...             return cls
... 

Ein skurriles Anwendungsbeispiel:

>>> class Citrus(Enum):
...     Lemon = 1
...     Lime = 2
... 
>>> Citrus.Lemon
Citrus.Lemon
>>> 
>>> Citrus(1)
Citrus.Lemon
>>> Citrus(5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __new__
KeyError: 5
>>> class Fruit(Citrus):
...     Apple = 3
...     Banana = 4
... 
>>> Fruit.Apple
Fruit.Apple
>>> Fruit.Lemon
Citrus.Lemon
>>> Fruit(1)
Citrus.Lemon
>>> Fruit(3)
Fruit.Apple
>>> "%d %s %r" % ((Fruit.Apple,)*3)
'3 Apple Fruit.Apple'
>>> Fruit(1) is Citrus.Lemon
True

Hauptmerkmale:

  • str(), int() Und repr() Alle erzeugen die nützlichste Ausgabe, die möglich ist, nämlich den Namen der Aufzählung, ihren ganzzahligen Wert und einen Python-Ausdruck, der zurück zur Aufzählung ausgewertet wird.
  • Vom Konstruktor zurückgegebene Aufzählungswerte sind streng auf die vordefinierten Werte beschränkt, keine versehentlichen Aufzählungswerte.
  • Aufzählungswerte sind Singletons;Sie können streng mit denen verglichen werden is

Die Lösung von Alec Thomas gefällt mir wirklich gut (http://stackoverflow.com/a/1695250):

def enum(**enums):
    '''simple constant "enums"'''
    return type('Enum', (object,), enums)

Es sieht elegant und sauber aus, aber es ist nur eine Funktion, die eine Klasse mit den angegebenen Attributen erstellt.

Mit einer kleinen Modifikation der Funktion können wir dafür sorgen, dass sie sich etwas „enumiger“ verhält:

NOTIZ:Ich habe die folgenden Beispiele erstellt, indem ich versuchte, das Verhalten von Pygtks neuem Stil 'Enums' zu reproduzieren (wie gtk.messagetype.warning)

def enum_base(t, **enums):
    '''enums with a base class'''
    T = type('Enum', (t,), {})
    for key,val in enums.items():
        setattr(T, key, T(val))

    return T

Dadurch wird eine Aufzählung basierend auf einem angegebenen Typ erstellt.Sie gewährt nicht nur Attributzugriff wie die vorherige Funktion, sondern verhält sich auch in Bezug auf Typen so, wie Sie es von einer Enum erwarten würden.Es erbt auch die Basisklasse.

Zum Beispiel ganzzahlige Aufzählungen:

>>> Numbers = enum_base(int, ONE=1, TWO=2, THREE=3)
>>> Numbers.ONE
1
>>> x = Numbers.TWO
>>> 10 + x
12
>>> type(Numbers)
<type 'type'>
>>> type(Numbers.ONE)
<class 'Enum'>
>>> isinstance(x, Numbers)
True

Eine weitere interessante Sache, die mit dieser Methode durchgeführt werden kann, ist die Anpassung spezifischer Verhaltensweisen durch Überschreiben integrierter Methoden:

def enum_repr(t, **enums):
    '''enums with a base class and repr() output'''
    class Enum(t):
        def __repr__(self):
            return '<enum {0} of type Enum({1})>'.format(self._name, t.__name__)

    for key,val in enums.items():
        i = Enum(val)
        i._name = key
        setattr(Enum, key, i)

    return Enum



>>> Numbers = enum_repr(int, ONE=1, TWO=2, THREE=3)
>>> repr(Numbers.ONE)
'<enum ONE of type Enum(int)>'
>>> str(Numbers.ONE)
'1'
def enum(*sequential, **named):
    enums = dict(zip(sequential, [object() for _ in range(len(sequential))]), **named)
    return type('Enum', (), enums)

Wenn Sie es nennen, ist das Ihr Problem, aber wenn Sie keine Objekte anstelle von Werten erstellen, können Sie Folgendes tun:

>>> DOG = enum('BARK', 'WALK', 'SIT')
>>> CAT = enum('MEOW', 'WALK', 'SIT')
>>> DOG.WALK == CAT.WALK
False

Wenn Sie andere hier aufgeführte Implementierungen verwenden (auch wenn Sie in meinem Beispiel benannte Instanzen verwenden), müssen Sie sicherstellen, dass Sie niemals versuchen, Objekte aus verschiedenen Enumerationen zu vergleichen.Denn hier ist eine mögliche Falle:

>>> DOG = enum('BARK'=1, 'WALK'=2, 'SIT'=3)
>>> CAT = enum('WALK'=1, 'SIT'=2)
>>> pet1_state = DOG.BARK
>>> pet2_state = CAT.WALK
>>> pet1_state == pet2_state
True

Huch!

Der neue Standard in Python ist PEP 435, sodass in zukünftigen Versionen von Python eine Enum-Klasse verfügbar sein wird:

>>> from enum import Enum

Um es jedoch jetzt verwenden zu können, können Sie es installieren Originalbibliothek Das motivierte das PEP:

$ pip install flufl.enum

Dann Sie kann es gemäß der Online-Anleitung verwenden:

>>> from flufl.enum import Enum
>>> class Colors(Enum):
...     red = 1
...     green = 2
...     blue = 3
>>> for color in Colors: print color
Colors.red
Colors.green
Colors.blue

Das Enum-Paket von PyPI Bietet eine robuste Implementierung von Enumerationen.In einer früheren Antwort wurde PEP 354 erwähnt;Dies wurde abgelehnt, aber der Vorschlag wurde umgesetzthttp://pypi.python.org/pypi/enum.

Die Nutzung ist einfach und elegant:

>>> from enum import Enum
>>> Colors = Enum('red', 'blue', 'green')
>>> shirt_color = Colors.green
>>> shirt_color = Colors[2]
>>> shirt_color > Colors.red
True
>>> shirt_color.index
2
>>> str(shirt_color)
'green'

Alexandrus Vorschlag, Klassenkonstanten für Aufzählungen zu verwenden, funktioniert recht gut.

Ich füge auch gerne ein Wörterbuch für jeden Konstantensatz hinzu, um eine für Menschen lesbare Zeichenfolgendarstellung zu finden.

Dies dient zwei Zwecken:a) Es bietet eine einfache Möglichkeit, Ihre Enumeration hübsch zu drucken, und b) das Wörterbuch gruppiert die Konstanten logisch, sodass Sie die Zugehörigkeit testen können.

class Animal:    
  TYPE_DOG = 1
  TYPE_CAT = 2

  type2str = {
    TYPE_DOG: "dog",
    TYPE_CAT: "cat"
  }

  def __init__(self, type_):
    assert type_ in self.type2str.keys()
    self._type = type_

  def __repr__(self):
    return "<%s type=%s>" % (
        self.__class__.__name__, self.type2str[self._type].upper())

Hier ist ein Ansatz mit einigen unterschiedlichen Merkmalen, die ich wertvoll finde:

  • Ermöglicht den Vergleich von > und < basierend auf der Reihenfolge in der Aufzählung, nicht auf der lexikalischen Reihenfolge
  • kann Elemente nach Name, Eigenschaft oder Index adressieren:x.a, x['a'] oder x[0]
  • unterstützt Slicing-Operationen wie [:] oder [-1]

und am wichtigsten verhindert Vergleiche zwischen Aufzählungen verschiedener Typen!

Basierend auf http://code.activestate.com/recipes/413486-first-class-enums-in-python.

Hier sind viele Ärztetests enthalten, um zu veranschaulichen, was an diesem Ansatz anders ist.

def enum(*names):
    """
SYNOPSIS
    Well-behaved enumerated type, easier than creating custom classes

DESCRIPTION
    Create a custom type that implements an enumeration.  Similar in concept
    to a C enum but with some additional capabilities and protections.  See
    http://code.activestate.com/recipes/413486-first-class-enums-in-python/.

PARAMETERS
    names       Ordered list of names.  The order in which names are given
                will be the sort order in the enum type.  Duplicate names
                are not allowed.  Unicode names are mapped to ASCII.

RETURNS
    Object of type enum, with the input names and the enumerated values.

EXAMPLES
    >>> letters = enum('a','e','i','o','u','b','c','y','z')
    >>> letters.a < letters.e
    True

    ## index by property
    >>> letters.a
    a

    ## index by position
    >>> letters[0]
    a

    ## index by name, helpful for bridging string inputs to enum
    >>> letters['a']
    a

    ## sorting by order in the enum() create, not character value
    >>> letters.u < letters.b
    True

    ## normal slicing operations available
    >>> letters[-1]
    z

    ## error since there are not 100 items in enum
    >>> letters[99]
    Traceback (most recent call last):
        ...
    IndexError: tuple index out of range

    ## error since name does not exist in enum
    >>> letters['ggg']
    Traceback (most recent call last):
        ...
    ValueError: tuple.index(x): x not in tuple

    ## enums must be named using valid Python identifiers
    >>> numbers = enum(1,2,3,4)
    Traceback (most recent call last):
        ...
    AssertionError: Enum values must be string or unicode

    >>> a = enum('-a','-b')
    Traceback (most recent call last):
        ...
    TypeError: Error when calling the metaclass bases
        __slots__ must be identifiers

    ## create another enum
    >>> tags = enum('a','b','c')
    >>> tags.a
    a
    >>> letters.a
    a

    ## can't compare values from different enums
    >>> letters.a == tags.a
    Traceback (most recent call last):
        ...
    AssertionError: Only values from the same enum are comparable

    >>> letters.a < tags.a
    Traceback (most recent call last):
        ...
    AssertionError: Only values from the same enum are comparable

    ## can't update enum after create
    >>> letters.a = 'x'
    Traceback (most recent call last):
        ...
    AttributeError: 'EnumClass' object attribute 'a' is read-only

    ## can't update enum after create
    >>> del letters.u
    Traceback (most recent call last):
        ...
    AttributeError: 'EnumClass' object attribute 'u' is read-only

    ## can't have non-unique enum values
    >>> x = enum('a','b','c','a')
    Traceback (most recent call last):
        ...
    AssertionError: Enums must not repeat values

    ## can't have zero enum values
    >>> x = enum()
    Traceback (most recent call last):
        ...
    AssertionError: Empty enums are not supported

    ## can't have enum values that look like special function names
    ## since these could collide and lead to non-obvious errors
    >>> x = enum('a','b','c','__cmp__')
    Traceback (most recent call last):
        ...
    AssertionError: Enum values beginning with __ are not supported

LIMITATIONS
    Enum values of unicode type are not preserved, mapped to ASCII instead.

    """
    ## must have at least one enum value
    assert names, 'Empty enums are not supported'
    ## enum values must be strings
    assert len([i for i in names if not isinstance(i, types.StringTypes) and not \
        isinstance(i, unicode)]) == 0, 'Enum values must be string or unicode'
    ## enum values must not collide with special function names
    assert len([i for i in names if i.startswith("__")]) == 0,\
        'Enum values beginning with __ are not supported'
    ## each enum value must be unique from all others
    assert names == uniquify(names), 'Enums must not repeat values'

    class EnumClass(object):
        """ See parent function for explanation """

        __slots__ = names

        def __iter__(self):
            return iter(constants)

        def __len__(self):
            return len(constants)

        def __getitem__(self, i):
            ## this makes xx['name'] possible
            if isinstance(i, types.StringTypes):
                i = names.index(i)
            ## handles the more normal xx[0]
            return constants[i]

        def __repr__(self):
            return 'enum' + str(names)

        def __str__(self):
            return 'enum ' + str(constants)

        def index(self, i):
            return names.index(i)

    class EnumValue(object):
        """ See parent function for explanation """

        __slots__ = ('__value')

        def __init__(self, value):
            self.__value = value

        value = property(lambda self: self.__value)

        enumtype = property(lambda self: enumtype)

        def __hash__(self):
            return hash(self.__value)

        def __cmp__(self, other):
            assert self.enumtype is other.enumtype, 'Only values from the same enum are comparable'
            return cmp(self.value, other.value)

        def __invert__(self):
            return constants[maximum - self.value]

        def __nonzero__(self):
            ## return bool(self.value)
            ## Original code led to bool(x[0])==False, not correct
            return True

        def __repr__(self):
            return str(names[self.value])

    maximum = len(names) - 1
    constants = [None] * len(names)
    for i, each in enumerate(names):
        val = EnumValue(i)
        setattr(EnumClass, each, val)
        constants[i] = val
    constants = tuple(constants)
    enumtype = EnumClass()
    return enumtype

Hier ist eine Variante dazu Die Lösung von Alec Thomas:

def enum(*args, **kwargs):
    return type('Enum', (), dict((y, x) for x, y in enumerate(args), **kwargs)) 

x = enum('POOH', 'TIGGER', 'EEYORE', 'ROO', 'PIGLET', 'RABBIT', 'OWL')
assert x.POOH == 0
assert x.TIGGER == 1

Diese Lösung ist eine einfache Möglichkeit, eine Klasse für die als Liste definierte Aufzählung zu erhalten (keine lästigen Ganzzahlzuweisungen mehr):

enumeration.py:

import new

def create(class_name, names):
    return new.classobj(
        class_name, (object,), dict((y, x) for x, y in enumerate(names))
    )

example.py:

import enumeration

Colors = enumeration.create('Colors', (
    'red',
    'orange',
    'yellow',
    'green',
    'blue',
    'violet',
))

Während der ursprüngliche Enum-Vorschlag PEP 354, wurde vor Jahren abgelehnt, es kommt immer wieder zur Sprache.Eine Art Enumeration sollte zu 3.2 hinzugefügt werden, wurde aber auf 3.3 verschoben und dann vergessen.Und jetzt gibt es eine PEP 435 Für die Aufnahme in Python 3.4 vorgesehen.Die Referenzimplementierung von PEP 435 ist flufl.enum.

Im April 2013 scheint es darüber einen allgemeinen Konsens zu geben etwas sollte in 3.4 zur Standardbibliothek hinzugefügt werden – sofern sich die Leute darauf einigen können, was dieses „Etwas“ sein soll.Das ist der schwierige Teil.Sehen Sie sich die beginnenden Threads an Hier Und Hier, und ein halbes Dutzend anderer Threads in den ersten Monaten des Jahres 2013.

Mittlerweile erscheinen jedes Mal, wenn dies auftaucht, eine Menge neuer Designs und Implementierungen auf PyPI, ActiveState usw. Wenn Ihnen das FLUFL-Design also nicht gefällt, versuchen Sie es mit einem PyPI-Suche.

Eine Variante (mit Unterstützung zum Abrufen des Namens eines Aufzählungswerts) zu Die nette Antwort von Alec Thomas:

class EnumBase(type):
    def __init__(self, name, base, fields):
        super(EnumBase, self).__init__(name, base, fields)
        self.__mapping = dict((v, k) for k, v in fields.iteritems())
    def __getitem__(self, val):
        return self.__mapping[val]

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

Numbers = enum(ONE=1, TWO=2, THREE='three')
print Numbers.TWO
print Numbers[Numbers.ONE]
print Numbers[2]
print Numbers['three']
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top