Je suis principalement développeur C #, mais je travaille actuellement sur un projet en Python.
Comment représenter l'équivalent d'un Enum en Python?
la source
Je suis principalement développeur C #, mais je travaille actuellement sur un projet en Python.
Comment représenter l'équivalent d'un Enum en Python?
Des énumérations ont été ajoutées à Python 3.4 comme décrit dans PEP 435 . Il a également été rétroporté à 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 et 2.4 sur pypi.
Pour des techniques Enum plus avancées, essayez la bibliothèque aenum (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 __order__
en python 2 ).
enum34
, faites$ pip install enum34
aenum
, faites$ pip install aenum
L'installation enum
(sans numéro) installera 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 équivalent:
class Animal(Enum):
ant = 1
bee = 2
cat = 3
dog = 4
Dans les versions antérieures, une façon de réaliser les énumérations est:
def enum(**enums):
return type('Enum', (), enums)
qui est utilisé comme ça:
>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'
Vous pouvez également facilement prendre en charge 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 ça:
>>> 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)
Cela écrase tout ce qui porte ce nom, mais il est utile pour rendre vos énumérations en sortie. Il lancera KeyError si le mappage inverse n'existe pas. Avec le premier exemple:
>>> Numbers.reverse_mapping['three']
'THREE'
**named
) de la fonction enum pour les anciennes versions prend en charge les valeurs personnalisées:enum("blue", "red", "green", black=0)
Avant PEP 435, Python n'avait pas d'équivalent mais vous pouviez implémenter le vôtre.
Moi, j'aime rester simple (j'ai vu des exemples horriblement complexes sur le net), quelque chose comme ça ...
Dans Python 3.4 ( PEP 435 ), vous pouvez faire de Enum la classe de base. Cela vous donne un peu de fonctionnalités supplémentaires, décrites dans le PEP. Par exemple, les membres enum sont distincts des entiers, et ils sont composés de a
name
et avalue
.Si vous ne souhaitez pas saisir les valeurs, utilisez le raccourci suivant:
Enum
les implémentations peuvent être converties en listes et sont itérables . L'ordre de ses membres est l'ordre de déclaration et n'a rien à voir avec leurs valeurs. Par exemple:la source
object()
.Voici une implémentation:
Voici son utilisation:
la source
__setattr__(self, name, value)
et peut-être__delattr__(self, name)
pour que si vous écrivez accidentellementAnimals.DOG = CAT
, cela ne réussisse pas en silence.Animals.DOG
; en outre, les valeurs des constats sont des chaînes, de sorte que les comparaisons avec ces constantes sont plus lentes que si, disons, des entiers étaient autorisés comme valeurs.setattr()
fonction dans la__init__()
méthode au lieu de la__getattr__()
méthode de surenchère . Je suppose que cela est censé fonctionner de la même manière: classe Enum (objet): def __init __ (self, enum_string_list): if type (enum_string_list) == list: for enum_string in enum_string_list: setattr (self, enum_string, enum_string) else: raise AttributeErrortry-except
bloc?Si vous avez besoin des valeurs numériques, voici le moyen le plus rapide:
Dans Python 3.x, vous pouvez également ajouter un espace réservé étoilé à la fin, qui absorbera toutes les valeurs restantes de la plage au cas où cela ne vous dérangerait pas de gaspiller de la mémoire et ne pouvez pas compter:
la source
La meilleure solution pour vous dépendrait de ce que vous attendez de votre faux
enum
.Enum simple:
Si vous n'avez besoin
enum
que d'une liste de noms identifiant différents éléments , la solution de Mark Harrison (ci-dessus) est excellente:L'utilisation d'un
range
permet également de définir n'importe quelle valeur de départ :En plus de ce qui précède, si vous avez également besoin que les éléments appartiennent à un conteneur quelconque, intégrez-les dans une classe:
Pour utiliser l'élément enum, vous devez maintenant utiliser le nom du conteneur et le nom de l'élément:
Enum complexe:
Pour de longues listes d'énumérations ou des utilisations plus complexes d'énumérations, ces solutions ne suffiront pas. Vous pouvez consulter la recette de Will Ware pour la simulation d'énumérations en Python publiée dans le livre de recettes Python . Une version en ligne de cela est disponible ici .
Plus d'informations:
PEP 354: Les énumérations en Python ont les détails intéressants d'une proposition d'énumération en Python et pourquoi elle a été rejetée.
la source
range
vous pouvez omettre le premier argument si c'est 0my_enum = dict(map(reversed, enumerate(str.split('Item0 Item1 Item2'))))
.my_enum
Peut ensuite être utilisé dans la recherche, par exemple,my_enum['Item0']
peut être un index dans une séquence. Vous voudrez peut-être encapsuler le résultat destr.split
dans une fonction qui lève une exception s'il y a des doublons.Flag1, Flag2, Flag3 = [2**i for i in range(3)]
Le modèle d'énumération typesafe utilisé dans Java pré-JDK 5 présente un certain nombre d'avantages. Tout comme dans la réponse d'Alexandru, vous créez une classe et les champs de niveau de classe sont les valeurs d'énumération; cependant, les valeurs enum sont des instances de la classe plutôt que de petits entiers. Cela a l'avantage que vos valeurs d'énumération ne se comparent pas par inadvertance à de petits entiers, vous pouvez contrôler la façon dont elles sont imprimées, ajouter des méthodes arbitraires si cela est utile et faire des assertions en utilisant isinstance:
Un récent fil sur python-dev a souligné qu'il y avait quelques bibliothèques d'énumérations dans la nature, notamment:
la source
Une classe Enum peut être une ligne.
Comment l'utiliser (recherche directe et inverse, clés, valeurs, éléments, etc.)
la source
in
mot - clé pour rechercher des membres, ce qui est bien. Exemple d'utilisation:'Claimed' in Enum(['Unclaimed', 'Claimed'])
Donc je suis d'accord. N'imposons pas la sécurité des types en Python, mais je voudrais me protéger contre les erreurs stupides. Alors qu'en pensons-nous?
Cela m'empêche de collision de valeurs dans la définition de mes énumérations.
Il y a un autre avantage pratique: les recherches inversées très rapides:
la source
Animal = Enum('horse', 'dog', 'cat')
. J'attrape également ValueError dans getattr en cas d'élément manquant dans self.values - il semble préférable de déclencher une AttributeError avec la chaîne de nom fournie à la place. Je n'ai pas pu faire fonctionner la métaclasse dans Python 2.7 sur la base de mes connaissances limitées dans ce domaine, mais ma classe Enum personnalisée fonctionne correctement avec les méthodes d'instance directes.Python n'a pas d'équivalent intégré à
enum
, et d'autres réponses ont des idées pour implémenter le vôtre (vous pouvez également être intéressé par la version par dessus dans le livre de recettes Python).Cependant, dans les situations où un
enum
serait demandé en C, je finis généralement par utiliser des chaînes simples : en raison de la façon dont les objets / attributs sont mis en œuvre, (C) Python est optimisé pour fonctionner très rapidement avec des chaînes courtes de toute façon, donc il n'y aurait pas ce ne sera pas vraiment un avantage de performance à utiliser des entiers. Pour vous prémunir contre les fautes de frappe / valeurs invalides, vous pouvez insérer des chèques aux endroits sélectionnés.(Un inconvénient par rapport à l'utilisation d'une classe est que vous perdez l'avantage de la saisie semi-automatique)
la source
Le 10/05/2013, Guido a accepté d'accepter PEP 435 dans la bibliothèque standard Python 3.4. Cela signifie que Python a enfin un support intégré pour les énumérations!
Un backport est 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:
Représentation:
Itération:
Accès programmatique:
Pour plus d'informations, reportez-vous à la proposition . La documentation officielle suivra probablement bientôt.
la source
Je préfère définir des énumérations en Python comme ceci:
C'est plus à l'abri des bogues que d'utiliser des entiers car vous n'avez pas à vous soucier de vous assurer que les entiers sont uniques (par exemple, si vous avez dit Dog = 1 et Cat = 1, vous seriez foutu).
C'est plus à l'abri des bogues que d'utiliser des chaînes puisque vous n'avez pas à vous soucier des fautes de frappe (par exemple, x == "catt" échoue silencieusement, mais x == Animal.Catt est une exception d'exécution).
la source
Utilisez-le comme ceci:
si vous voulez juste des symboles uniques et ne vous souciez pas des valeurs, remplacez cette ligne:
avec ça:
la source
enum(names)
àenum(*names)
- alors vous pourriez supprimer la parenthèse supplémentaire lors de l'appel.À partir de Python 3.4, il y aura un support officiel pour les énumérations. Vous pouvez trouver de la documentation et des exemples ici sur la page de documentation de Python 3.4 .
la source
Hmmm ... Je suppose que la chose la plus proche d'une énumération serait un dictionnaire, défini comme ceci:
ou
Ensuite, vous pouvez utiliser le nom symbolique pour les constantes comme ceci:
Il existe d'autres options, comme une liste de tuples ou un tuple de tuples, mais le dictionnaire est le seul qui vous offre un moyen "symbolique" (chaîne constante) d'accéder à la valeur.
Edit: j'aime aussi la réponse d'Alexandru!
la source
Une autre implémentation très simple d'une énumération en Python, utilisant
namedtuple
:Ou bien,
Comme la méthode ci-dessus qui sous-classe
set
, cela permet:Mais a plus de flexibilité car il peut avoir différentes clés et valeurs. Ceci permet
pour agir comme prévu si vous utilisez la version qui remplit des valeurs numériques séquentielles.
la source
Ce que j'utilise:
Comment utiliser:
Cela vous donne donc des constantes entières comme state.PUBLISHED et les deux tuples à utiliser comme choix dans les modèles Django.
la source
davidg recommande d'utiliser des dict. Je voudrais aller plus loin et utiliser des ensembles:
Vous pouvez maintenant tester si une valeur correspond à l'une des valeurs de l'ensemble comme ceci:
comme dF, cependant, j'utilise généralement des constantes de chaîne à la place des énumérations.
la source
Rester simple:
Alors:
la source
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ées les unes aux autres, mais n'ont pas de valeur particulière; vous ne pouvez pas les utiliser comme valeur entière. (J'ai résisté à cela au début parce que je suis habitué aux énumérations C, 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 dans l'ensemble, je pense que c'est une victoire .) Chaque énumération est une valeur unique. Vous pouvez imprimer des énumérations, vous pouvez les parcourir, vous pouvez tester qu'une valeur d'énumération est "dans" l'énumération. C'est assez complet et lisse.
Edit (cfi): Le lien ci-dessus n'est pas compatible Python 3. Voici mon port d'enum.py vers Python 3:
la source
.__int__()
méthode devrait lever une exception pour une énumération; mais il devrait y avoir un moyen d'en retirer la valeur. Et il devrait être possible de définir des valeurs entières spécifiques au moment de la définition de la classe, afin que vous puissiez utiliser une énumération pour des choses comme les constantes dustat
module.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 souhaitais justement sont une définition d'énumération concise, la possibilité de créer librement des instances de l'énumération par une valeur entière ou une chaîne, et une
repr
présentation utile . Voici ce que j'ai fini avec:Un exemple fantaisiste de l'utiliser:
Principales caractéristiques:
str()
,int()
etrepr()
tous produisent la sortie la plus utile possible, respectivement le nom de l'énumération, sa valeur entière et une expression Python qui réévalue l'énumération.is
la source
__instancecheck__
méthode. Les classes ne sont pas des collections d'instances, c'est donc1 in Fruit
absurde. Cependant, la version liée prend en chargeisinstance(1, Fruit)
ce qui serait plus correct en termes de notion de classes et d'instances.Le nouveau standard en Python est PEP 435 , donc une classe Enum sera disponible dans les futures versions de Python:
Cependant, pour commencer à l'utiliser maintenant, vous pouvez installer la bibliothèque d'origine qui a motivé le PEP:
Ensuite, vous pouvez l'utiliser selon son guide en ligne :
la source
Si vous le nommez, c'est votre problème, mais sinon la création d'objets au lieu de valeurs vous permet de faire ceci:
Lorsque vous utilisez d'autres implémentations situé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 d'énumérations différentes. Car voici un écueil possible:
Oui!
la source
J'aime vraiment la solution d'Alec Thomas (http://stackoverflow.com/a/1695250):
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 de la fonction, nous pouvons l'amener à agir un peu plus 'enumy':
Cela crée une énumération basée sur un type spécifié. En plus de donner un accès aux attributs comme la fonction précédente, il se comporte comme vous vous attendez à ce qu'un Enum respecte les types. Il hérite également de la classe de base.
Par exemple, les énumérations entières:
Une autre chose intéressante qui peut être faite avec cette méthode est de personnaliser un comportement spécifique en remplaçant les méthodes intégrées:
la source
Le package enum de PyPI fournit une implémentation robuste des énumérations. Une réponse antérieure mentionnait le PEP 354; cela a été rejeté mais la proposition a été mise en œuvre http://pypi.python.org/pypi/enum .
L'utilisation est simple et élégante:
la source
La suggestion d'Alexandru d'utiliser des constantes de classe pour les énumérations fonctionne très bien.
J'aime également ajouter un dictionnaire pour chaque ensemble de constantes pour rechercher une représentation de chaîne lisible par l'homme.
Cela sert à deux fins: a) il fournit un moyen simple d'imprimer votre énumération et b) le dictionnaire regroupe logiquement les constantes afin que vous puissiez tester l'appartenance.
la source
Voici une approche avec quelques caractéristiques différentes que je trouve précieuses:
et surtout empêche les comparaisons entre des énumérations de différents types !
Basé étroitement sur http://code.activestate.com/recipes/413486-first-class-enums-in-python .
De nombreux doctests inclus ici pour illustrer ce qui est différent dans cette approche.
la source
Voici une variante de la solution d'Alec Thomas :
la source
Cette solution est un moyen simple d'obtenir une classe pour l'énumération définie comme une liste (plus d'affectations entières ennuyeuses):
enumeration.py:
example.py:
la source
type(class_name, (object,), dict(...))
place?Alors que la proposition d'énumération originale, PEP 354 , a été rejetée il y a des années, elle revient sans cesse. Une sorte d'énumération devait être ajoutée à la 3.2, mais elle a été repoussée à la 3.3 et ensuite oubliée. Et maintenant, il y a un PEP 435 destiné à être inclus dans Python 3.4. L'implémentation de référence du PEP 435 est
flufl.enum
.En avril 2013, il semble y avoir un consensus général selon lequel quelque chose devrait être ajouté à la bibliothèque standard en 3.4 - tant que les gens peuvent s'entendre sur ce que ce «quelque chose» devrait être. Voilà la partie difficile. Voir les discussions commençant ici et ici , et une demi-douzaine d'autres discussions dans les premiers mois de 2013.
Pendant ce temps, à chaque fois que cela arrive, une multitude de nouvelles conceptions et implémentations apparaissent sur PyPI, ActiveState, etc., donc si vous n'aimez pas la conception FLUFL, essayez une recherche PyPI .
la source
Utilisez le suivant.
Je l'ai utilisé pour les choix de modèles de Django , et cela a l'air très pythonique. Ce n'est pas vraiment un Enum, mais il fait le travail.
la source