Comment puis-je représenter un "Enum" en Python?

je suis principalement un développeur C#, mais je travaille actuellement sur un projet en Python.

Comment puis-je représenter l'équivalent d'un Enum en Python?

1146
demandé sur sectrean 2008-08-31 19:55:47

30 réponses

Enums ont été ajoutés à Python 3.4 comme décrit dans PEP 435 . Il a également été porté à 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, et 2.4 sur pypi.

Pour les plus avancés Enum techniques d'essayer de la aenum bibliothèque (2.7, 3.3+, même auteur que enum34 . Le Code n'est pas parfaitement compatible entre py2 et py3, par exemple vous aurez besoin de __order__ en python 2 ).

  • pour utiliser enum34 , do $ pip install enum34
  • pour utiliser aenum , do $ pip install aenum

installer enum (aucun numéro) va installer une version complètement différente et incompatible.


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)

ou l'équivalent:

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

dans les versions précédentes, une façon de l'accomplissement des enums est:

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

qui est utilisé comme ainsi:

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

vous pouvez aussi facilement soutenir l'énumération automatique avec quelque chose comme ceci:

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

et utilisé comme ainsi:

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

la prise en charge de la conversion des valeurs en noms peut être ajoutée de cette façon:

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)

ceci écrira tout avec ce nom, mais il est utile pour rendre vos énums en sortie. Il lancera KeyError si la cartographie inverse n'existe pas. Avec le premier exemple:

>>> Numbers.reverse_mapping['three']
'THREE'
2362
répondu Alec Thomas 2017-05-23 11:47:32

avant PEP 435, Python n'avait pas d'équivalent mais vous pouviez implémenter le vôtre.

moi-même, j'aime le garder simple (j'ai vu quelques exemples horriblement complexes sur le net), quelque chose comme ça ...

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

en Python 3.4 ( PEP 435 ), vous pouvez faire Enum la classe de base. Cela vous donne un peu de fonctionnalité supplémentaire, décrite dans le PEP. Par exemple, une énumération des membres sont distincts entiers, et ils sont composés d'un name et un value .

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

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

print(Animal.DOG.value)
# 1

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

si vous ne voulez pas taper les valeurs, utilisez le raccourci suivant:

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

Enum les implémentations peuvent être converties en listes et sont itérables . L'ordre de ses membres est de l'ordre de déclaration, et n'a rien à voir avec leurs valeurs. Par exemple:

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
729
répondu Jundiaius 2018-06-22 14:26:56

Voici une implémentation:

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

voici son usage:

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

print(Animals.DOG)
299
répondu shahjapan 2016-07-18 14:08:51

si vous avez besoin des valeurs numériques, voici le moyen le plus rapide:

dog, cat, rabbit = range(3)

En Python 3.x vous pouvez également ajouter un espace réservé starred à la fin, qui absorbera toutes les valeurs restantes de la plage dans le cas où vous ne vous souciez pas de gaspiller la mémoire et ne pouvez pas compter:

dog, cat, rabbit, horse, *_ = range(100)
188
répondu Mark Harrison 2017-05-05 08:48:49

la meilleure solution pour vous dépendrait de ce que vous exigez de votre faux enum .

Simple enum:

si vous avez besoin de la enum comme seule une liste de noms identifier différents articles , la solution par marque Harrison (ci-dessus) est grande:

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

L'utilisation d'un range vous permet également de définir n'importe quelle valeur de départ :

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

en plus de ce qui précède, si vous exigez également que les articles appartiennent à un conteneur d'une sorte, alors les intégrer dans une classe:

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

utilisation De l'enum élément, vous devez utiliser le nom du conteneur et le nom de l'élément:

stype = Stationery.Pen

Complexe enum:

pour de longues listes d'enum ou des utilisations plus compliquées d'enum, ces solutions ne suffiront pas. Vous pouvez consulter la recette de Will Ware pour simulant des énumérations en Python publiée dans le Python Cookbook . Une version en ligne est disponible ici .

plus d'informations:

PEP 354: énumérations en Python a les détails intéressants d'une proposition pour enum en Python et pourquoi il a été rejeté.

120
répondu Ashwin 2014-04-24 11:25:58

le modèle enum de typesafe qui a été utilisé dans Java pre-JDK 5 a un certain nombre d'avantages. Un peu comme dans la réponse D'Alexandru, vous créez un les champs de classe et de niveau de classe sont les valeurs enum; cependant, les valeurs enum les valeurs sont des instances de la classe plutôt que de petits nombres. Cela a l'avantage que vos valeurs d'enum ne comparent pas par inadvertance égale pour les petits entiers, vous pouvez contrôler comment ils sont imprimés, ajouter arbitraire méthodes si c'est utile et faire des assertions en utilisant isinstance:

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

Un récent thread sur python-dev , a souligné il y a quelques enum bibliothèques à l'état sauvage, y compris:

76
répondu Aaron Maenpaa 2010-11-30 03:56:22

une classe Enum peut être une doublure.

class Enum(tuple): __getattr__ = tuple.index

comment l'utiliser (recherche avant et arrière, clés, valeurs, éléments, 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)]
58
répondu Zoetic 2012-02-09 20:46:33

Python n'a pas d'équivalent intégré à enum , et d'autres réponses ont des idées pour mettre en œuvre votre propre (vous pouvez également être intéressé par la sur la version supérieure dans le livre de cuisine Python).

cependant, dans les situations où un enum serait appelé en C, je finis habituellement par en utilisant simplement des chaînes de caractères : en raison de la façon dont les objets/attributs sont implémentés, (C)Python est optimisé pour fonctionner très rapidement avec des chaînes courtes de toute façon, donc il n'y aurait pas vraiment d'avantage de performance à utiliser des entiers. Pour vous prémunir contre les fautes de frappe / valeurs invalides, vous pouvez insérer des vérifications dans des endroits choisis.

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

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

(un inconvénient par rapport à l'utilisation d'une classe est que vous perdez le bénéfice de l'autocomplete)

45
répondu dF. 2008-09-01 13:26:06

donc, je suis d'accord. Ne faisons pas appliquer la sécurité de type en Python, mais je voudrais me protéger contre les erreurs stupides. Alors, que faisons-nous penser de cela?

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

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

Il m'empêche de valeur-collision dans la définition de mes énumérations.

>>> Animal.Cat
2

il y a un autre avantage pratique: les recherches inversées vraiment rapides:

def name_of(self, i):
    return self.values[i]
45
répondu royal 2014-04-28 23:24:07

le 10-05-2013, Guido a accepté d'accepter PEP 435 dans la bibliothèque standard Python 3.4. Cela signifie que Python a finalement le support de builtin pour les énumérations!

il y a un backport disponible pour Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, et 2.4. C'est sur Pypi comme enum34 .

déclaration:

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

représentation:

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

Itération:

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

l'accès Programmatique:

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

pour plus d'information, voir the proposal . La documentation officielle suivra probablement bientôt.

31
répondu Danilo Bargen 2013-06-18 11:22:30

je préfère définir enums en Python comme ceci:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

il est plus à l'épreuve des bogues que l'utilisation d'entiers puisque vous n'avez pas à vous soucier de s'assurer que les entiers sont uniques (par exemple, si vous avez dit Dog = 1 et Cat = 1 vous seriez vissé).

c'est plus à l'épreuve des bogues que d'utiliser des chaînes de caractères car vous n'avez pas à vous soucier des fautes de frappe (par ex. x == "catt" échoue silencieusement, mais x == Animal.Catt est une exception d'exécution).

30
répondu mbac32768 2010-11-29 02:05:55
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()

utilisez - le comme ceci:

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

si vous voulez juste des symboles uniques et ne vous souciez pas des valeurs, remplacez cette ligne:

__metaclass__ = M_add_class_attribs(enumerate(names))

avec ceci:

__metaclass__ = M_add_class_attribs((object(), name) for name in names)
29
répondu user18695 2013-02-03 01:57:51

un autre, très simple, implémentation d'un enum en Python, en utilisant namedtuple :

from collections import namedtuple

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

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

ou, alternativement,

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

comme la méthode ci-dessus qui sous-classe set , cela permet:

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

mais a plus de flexibilité car il peut avoir différentes clés et valeurs. Cela permet

MyEnum.FOO < MyEnum.BAR

pour agir comme prévu si vous utilisez la version qui remplit en séquence nombre de valeurs.

21
répondu agf 2011-08-07 07:44:53

Hmmm... Je suppose que la chose la plus proche d'un enum serait un dictionnaire, défini comme ceci:

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

ou

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

Ensuite, vous pouvez utiliser le nom symbolique pour les constantes comme ceci:

mymonth = months['January']

Il y a d'autres options, comme une liste de tuples, ou un tuple de tuples, mais le dictionnaire est le seul qui vous donne un "symbolique" (chaîne constante) pour accéder à l' valeur.

Edit: j'aime aussi la réponse D'Alexandru!

19
répondu dguaraglia 2008-08-31 16:09:53

ce que j'utilise:

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

comment utiliser:

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

donc cela vous donne des constantes entières comme l'état.Publié et les deux-tuples à utiliser comme choix dans les modèles Django.

17
répondu 2 revs, 2 users 90%Luciano Ramalho 2009-11-17 19:46:02

davidg recommande l'utilisation de dicts. J'irais un peu plus loin et j'utiliserais des ensembles:

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

maintenant vous pouvez tester si une valeur correspond à l'une des valeurs dans l'ensemble comme ceci:

if m in months:

comme dF, cependant, j'utilise habituellement des constantes de chaîne à la place des énums.

15
répondu tuxedo 2008-09-02 03:20:30

de Python 3.4 il y aura un soutien officiel pour enums. Vous pouvez trouver de la documentation et des exemples ici sur la page de documentation de Python 3.4 .

les énumérations sont créées en utilisant la syntaxe de classe, ce qui les rend faciles à lire et à écrire. Une méthode alternative de création est décrite dans Fonctionnelle de l'API. Pour définir une énumération, sous-classe Enum comme suit:

from enum import Enum
class Color(Enum):
     red = 1
     green = 2
     blue = 3
15
répondu Cyril Gandon 2016-08-20 02:53:04

C'est le meilleur que j'ai vu: "Enums de première classe en Python"

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

Il vous donne une classe, et la classe contient toutes les énumérations. Les énumérations peuvent être comparés les uns aux autres, mais n'ont pas de valeur particulière; vous ne pouvez pas les utiliser comme une valeur entière. (J'y ai résisté au début parce que je suis habitué aux nombres, qui sont des valeurs entières. Mais si vous ne pouvez pas l'utiliser comme un entier, vous ne pouvez pas l'utiliser comme un entier, par erreur donc, globalement, je pense que c'est une victoire. Chaque enum est une valeur unique. Vous pouvez imprimer les énumérations, vous pouvez effectuer une itération sur eux, vous pouvez tester qu'une valeur d'enum est "dans" l'enum. C'est assez complète et soignée.

Edit (cfi): le lien ci-dessus n'est pas compatible Python 3. Voici mon port de enum.py à 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)
14
répondu cfi 2011-10-13 10:29:37

j'ai eu l'occasion d'avoir besoin d'une classe Enum, dans le but de décoder un format de fichier binaire. Les fonctionnalités que je me suis trouvé à vouloir est concise enum définition, la capacité de créer librement des instances de l'enum par valeur entière ou chaîne, et une utile repr esentation. Voici ce que j'ai:

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

Un lunatique exemple d'utilisation:

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

principales caractéristiques:

  • str() , int() et repr() produisent tous la sortie la plus utile possible, respectivement le nom de l'énumération, sa valeur entière, et une expression Python qui évalue retour à l'énumération.
  • les valeurs énumérées retournées par le constructeur sont strictement limitées aux valeurs prédéfinies, pas de valeurs enum accidentelles.
  • les valeurs énumérées sont des singletons; elles peuvent être strictement comparées avec is
11
répondu TokenMacGuy 2013-05-18 19:06:50

Keep it simple:

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

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

puis:

DIRECTION = Enum(('UP', 'DOWN', 'LEFT', 'RIGHT'))
DIRECTION.DOWN
1
11
répondu danger89 2016-02-10 18:09:38

j'aime vraiment la solution D'Alec Thomas (http://stackoverflow.com/a/1695250):

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

c'est élégant et propre, mais c'est juste une fonction qui crée une classe avec les attributs spécifiés.

avec une petite modification à la fonction, nous pouvons l'amener à agir un peu plus "enumy":

NOTE: j'ai créé les exemples suivants en essayant de reproduire le comportement du nouveau style de pygtk "enums" (comme Gtk.MessageType.Avertissement)

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

Cela crée un enum basé sur un type spécifié. En plus de donner un accès attribut comme la fonction précédente, il se comporte comme on s'y attendrait d'un Enum en ce qui concerne les types. Il hérite aussi de la classe de base.

par exemple, entier enums:

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

une autre chose intéressante qui peut être faite avec cette méthode est de personnaliser le comportement spécifique par dérogation aux méthodes intégrées:

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'
9
répondu bj0 2012-01-18 06:09:18

le nouveau standard en Python est PEP 435 , ainsi une classe Enum sera disponible dans les futures versions de Python:

>>> from enum import Enum

Cependant pour commencer à l'utiliser maintenant, vous pouvez installer le bibliothèque d'origine qui a motivé le PEP:

#sudo pip install flufl.enum   //or #sudo easy_install flufl.enum

alors vous peut l'utiliser comme son guide en ligne :

>>> 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
8
répondu Riaz Rizvi 2013-05-19 22:32:20
def enum(*sequential, **named):
    enums = dict(zip(sequential, [object() for _ in range(len(sequential))]), **named)
    return type('Enum', (), enums)

si vous le nommez, est votre problème, mais si ne pas créer des objets au lieu de valeurs vous permet de le faire:

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

lorsque vous utilisez d'autres implémentations indiquées ici (également lorsque vous utilisez des instances nommées dans mon exemple), vous devez être sûr de ne jamais essayer de comparer des objets provenant de différents enums. Car voici un piège possible:

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

Beurk!

8
répondu estani 2014-11-11 09:26:34

le paquet enum de PyPI fournit une mise en œuvre robuste de enums. Une réponse antérieure mentionnait le PEP 354; cette proposition a été rejetée, mais la proposition a été mise en œuvre. http://pypi.python.org/pypi/enum .

L'Usage est facile et élégant:

>>> 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'
7
répondu Ashwin 2013-05-18 09:45:42

la suggestion D'Alexandru d'utiliser les constantes de classe pour les énums fonctionne assez bien.

j'aime aussi ajouter un dictionnaire pour chaque ensemble de constantes pour rechercher une représentation de chaîne lisible par l'homme.

ceci sert deux buts: a) il fournit un moyen simple pour pretty-print your enum et b) le dictionnaire groupe logiquement les constantes de sorte que vous pouvez tester pour l'adhésion.

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())
6
répondu Rick Harris 2008-09-19 03:37:43

Voici une variante de solution D'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
4
répondu Roy Hyunjin Han 2017-05-23 12:18:27

Cette solution est un moyen simple d'obtenir une classe pour l'énumération de la liste (pas plus gênant entier affectations):

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',
))
4
répondu Michael Truog 2011-09-18 01:40:04

alors que la proposition originale d'enum, PEP 354 , a été rejetée il y a des années, elle revient sans cesse. Une sorte d'enum était destiné à être ajouté à 3.2, mais il a été repoussé à 3.3 et puis oublié. Et maintenant il y a un PEP 435 destiné à être inclus dans Python 3.4. La mise en œuvre de référence du PEP 435 est flufl.enum .

en avril 2013, il semble y avoir un consensus général sur le fait que quelque chose devrait être ajouté à la bibliothèque standard en 3.4-aussi longtemps que les gens peuvent s'entendre sur ce que" quelque chose " devrait être. C'est la partie la plus difficile. Voir les fils commençant ici et ici , et une demi-douzaine d'autres fils dans les premiers mois de 2013.

pendant ce temps, à chaque fois que cela se produit, un grand nombre de nouveaux designs et implémentations apparaissent sur PyPI, ActiveState, etc. donc si vous n'aimez pas le FLUFL design, essayez de 1519180920" PyPI de recherche .

4
répondu abarnert 2013-04-19 01:16:20

Voici une approche avec quelques caractéristiques différentes que je trouve précieux:

  • permet > et < comparaison basée sur l'ordre enum, pas l'ordre lexical
  • peut adresser l'élément par nom, propriété ou index: X. a, x['a'] ou x[0]
  • supporte les opérations de tranchage comme [:] ou [-1]

et surtout empêche les comparaisons entre les énums de différents types !

basé sur http://code.activestate.com/recipes/413486-first-class-enums-in-python .

de nombreux documents inclus ici pour illustrer ce qui est différent de cette approche.

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
4
répondu Chris Johnson 2013-06-19 21:30:47

j'avais besoin de quelques constantes symboliques en pyparsing pour représenter l'associativité gauche et droite des opérateurs binaires. J'ai utilisé des constantes de classe comme ceci:

# an internal class, not intended to be seen by client code
class _Constants(object):
    pass


# an enumeration of constants for operator associativity
opAssoc = _Constants()
opAssoc.LEFT = object()
opAssoc.RIGHT = object()

maintenant quand le code client veut utiliser ces constantes, ils peuvent importer la totalité de l'enum en utilisant:

import opAssoc from pyparsing

les énumérations sont uniques, elles peuvent être testées avec ' is 'au lieu de'==', elles ne prennent pas une grande empreinte dans mon code pour un concept mineur, et elles sont facilement importé dans le code client. Ils ne supportent aucun comportement de fantaisie str (), mais jusqu'à présent qui est dans la catégorie YAGNI .

2
répondu Paul McGuire 2010-04-16 12:40:44