2012-10-10 2 views
10

Il y a eu quelques questions sur la façon d'implémenter des énumérations en Python. La plupart des solutions finissent par être plus ou moins équivalent à quelque chose comme ceci:Conception Pythonic sans énumération

class Animal: 
    DOG=1 
    CAT=2 

D'autres ont suggérer des moyens plus complexes de la construction énumérations, mais ultimatly l'ont tendance à ressembler à cet exemple quand tout est dit et fait. Basé sur mon expérience en Java et en C#, je peux penser à toutes sortes d'utilisations pour un tel idiome. Cependant, il ne semble pas être très Pythonic. En fait, il semble que chaque fois que quelqu'un demande pourquoi il n'y a pas d'énumération dans Python, vous avez tendance à vous plaindre de la façon dont il n'y a aucune raison d'essayer de forcer la sécurité du type de compilation dans un langage comme Python, ou comment les conceptions qui nécessitent des énumérations sont de mauvaises odeurs en Python.

Ma question n'est pas de savoir comment implémenter des enums en Python, mais comment en général les gens abordent des solutions à des problèmes qui se prêtent aux énumérations de façon Pythonienne. En d'autres termes, comment résoudriez-vous un problème qui se prête à un type de données avec un ensemble discret de valeurs possibles sans porter votre solution Java/C# à Python.

+2

Je ne vois pas pourquoi l'exemple que vous avez donné est un-Pythonic. Et si je mettais en œuvre une machine d'état, je profiterais des fonctions en tant qu'objets et définirais mes états en tant que fonctions. –

+0

Je ne sais pas car il y a quelque chose de particulièrement faux sur la façon dont l'énumération a été implémentée, mais j'ai juste l'impression que certaines personnes dans la communauté Python considèrent les solutions qui nécessitent des énumérations comme non-Pythonic. Votre suggestion qu'une machine d'état soit mise en application utilisant des fonctions de première classe comme état est un bon début à une réponse cependant! – jlund3

Répondre

4

PEP 435 vient d'être accepté, ce qui ajoute le paquet enum à la bibliothèque standard avec la classe Enum et d'autres dérivés comme IntEnum. Cela signifie que, à partir de Python 3.4, la façon "pythonique" d'utiliser des énumérations dans un dessin est avec ce paquet.Il ressemblera à quelque chose comme ceci:

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

>>> print(Color.red) 
Color.red 

>>> print(Color.red.name) 
red 

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

La principale caractéristique de cette conception est que les clés peuvent rejeter la comparaison où il n'a pas de sens (contrairement aux clés de chaîne proposées dans d'autres réponses), que les clés ont assez d'impression sans rapport avec la valeur de la clé, et en donnant aux propriétés clés des propriétés spéciales en définissant des méthodes sur la sous-classe Enum, en plus de la propriété de lancer une erreur explicite et compréhensible si l'utilisateur essaie d'utiliser une clé illégale de la classe Enum.

+0

Seul inconvénient est: ne fonctionne pas avant Python 3.4, qui n'est plus dans une distribution majeure (Oct 2013). Alors attendons et buvons du thé. – nerdoc

+1

Un backport de PEP 435 pour Python 2.4+ est disponible sur PyPI en tant que [enum34] (https://pypi.python.org/pypi/enum34/). Bien qu'il ne soit pas officiel, il est écrit par le co-auteur du PEP 435, Ethan Furman. – tawmas

1

N'étant pas issu d'un arrière-plan C# ou Java, je ne comprends toujours pas cet intérêt pour les énumérations. D'après ce que je comprends, je retomberais probablement dans le style django, en mettant les constantes nécessaires dans un fichier de paramètres et en les important si nécessaire.

settings.py/animals.py

DOG = 1 
CAT = 2 

autre fichier:

from animals import CAT, DOG 

ou même ...

settings.py

ANIMALS = { "DOG": 1, "CAT": 2} 

autre fichier:

from settings import ANIMALS 

my_animal = "FROG" 
if my_animal not in ANIMALS.keys(): 
    print "new species discovered!" 
+2

'si mon_animal n'est pas dans ANIMALS.keys():' est en fait (a) plus long et (b) moins efficace, que le plus simple cocher 'if my_animal not in ANIMALS'. – Amber

+2

Je pense que cet exemple met en évidence pourquoi les personnes ayant une exposition à Java/C# veulent enums en Python. C'est une chose d'accepter le typage du canard où tout ce qui sait comment le charlatan est accepté. C'est un sujet tout à fait différent (à un gars Java/C#) de dire que votre langage permet des fautes de frappe simples pour vous vider. Par exemple, supposons que j'ai tapé my_animal = "dog" au lieu de my_animal = "DOG" et en quelque sorte découvert une nouvelle espèce? Avoir une énumération où vous pouvez juste dire my_animal = Animal.DOG vous évite un peu de douleur car vous aurez une erreur AttributeError dans votre visage au moment de l'exécution plutôt qu'un bug d'espèce découvert. – jlund3

+0

Certes, je veux juste montrer explicitement ce qui se fait, car les nouveaux venus ne sont pas clairs ce qui se fait dans l'alternative. – monkut

3

Depuis cordes Python sont immuables (et Python les stagiaires comme il le juge nécessaire), il n'y a pas vraiment un avantage spécifique à l'utilisation énumérations numériques pour des ensembles de choses. Au lieu de cela, vous pouvez simplement utiliser un frozenset ou tuple de chaînes (selon si vous vous souciez de commander).

Si, d'un autre côté, ce qui vous intéresse, c'est un espace de noms, ce qui fait que les attributs d'une classe fonctionnent bien.

Comme mentionné dans les commentaires, si vous cherchez quelque chose comme une implémentation de machine d'état, les fonctions de première classe sont votre ami.

+0

..Et si vous vous souciez de namespacing * et * vous utilisez 3.3, il y a cette recette: http://code.activestate.com/recipes/578279-using-chainmap-for-embedded-namespaces/?in=lang -python –

+1

Si vous utilisez 3.3, vous pouvez simplement utiliser ['types.SimpleNamespace'] (http://docs.python.org/py3k/whatsnew/3.3.html#simplenamespace) par contre :) –

-1

Je ne crois pas à Enums en C#. Pour la State-Machine, vous avez le pattern Strategy. Plutôt que de regarder l'état d'un objet et de décider quoi faire. Encapsuler la logique de ce qu'il faut faire dans l'objet lui-même.

Ceci est une approche naturelle en Python et l'introduction d'enums est juste encourageant mauvais code.

+0

La question ne parle pas spécialement des machines d'état. Je suis d'accord que dans les deux enums C# et Python ne sont pas la façon de procéder à l'implémentation d'une machine d'état. Cependant, nous parlons plutôt de représenter un type qui a un petit ensemble de valeurs nommées qu'il peut prendre. Pour les problèmes impliquant ce type de type de données, les enums sont très utiles. – jlund3

+0

Pour moi, cela ressemble à l'héritage. Un type qui a un petit ensemble de valeurs nommées. Encore une fois, Python a bien compris la première fois en n'incluant pas les enums. –

+0

Utiliser l'héritage pour créer plusieurs types de données avec la même classe parente n'est pas la même chose que d'avoir un seul type de données avec un petit ensemble de valeurs possibles ... – jlund3