Come dovrei emulare e / o evitare al meglio enum's in Python? [duplicare]
Domanda
Questa domanda ha già una risposta qui:
- Come posso rappresentare un 'Enum' in Python? 43 risposte
Ho usato una piccola classe per emulare Enum in alcuni progetti Python. C'è un modo migliore o ha più senso in alcune situazioni?
Codice classe qui:
class Enum(object):
'''Simple Enum Class
Example Usage:
>>> codes = Enum('FOO BAR BAZ') # codes.BAZ will be 2 and so on ...'''
def __init__(self, names):
for number, name in enumerate(names.split()):
setattr(self, name, number)
Soluzione
Ci sono molte buone discussioni qui .
Altri suggerimenti
Gli enum sono stati proposti per l'inclusione nella lingua prima, ma sono stati respinti (vedi http://www.python.org/dev/peps/pep-0354/ ), sebbene ci siano pacchetti esistenti che potresti usare invece di scrivere la tua implementazione:
- enum: http://pypi.python.org/pypi/enum
- SymbolType (non esattamente uguale a enums, ma comunque utile): http: //pypi.python.org/pypi/SymbolType
- O fai semplicemente una ricerca
Il caso enum più comune sono i valori enumerati che fanno parte di un modello di progettazione di stato o strategia. Gli enum sono stati specifici o specifiche strategie opzionali da utilizzare. In questo caso, fanno quasi sempre parte di una definizione di classe
class DoTheNeedful( object ):
ONE_CHOICE = 1
ANOTHER_CHOICE = 2
YET_ANOTHER = 99
def __init__( self, aSelection ):
assert aSelection in ( self.ONE_CHOICE, self.ANOTHER_CHOICE, self.YET_ANOTHER )
self.selection= aSelection
Quindi, in un client di questa classe.
dtn = DoTheNeeful( DoTheNeeful.ONE_CHOICE )
Quello che vedo più spesso è questo, nel contesto del modulo di livello superiore:
FOO_BAR = 'FOO_BAR'
FOO_BAZ = 'FOO_BAZ'
FOO_QUX = 'FOO_QUX'
... e successive ...
if something is FOO_BAR: pass # do something here
elif something is FOO_BAZ: pass # do something else
elif something is FOO_QUX: pass # do something else
else: raise Exception('Invalid value for something')
Nota che l'uso di è
piuttosto che ==
sta correndo un rischio qui - presume che la gente stia usando your_module.FOO_BAR
piuttosto che la stringa 'FOO_BAR'
(che normalmente sarà internata in modo tale che sia
corrisponderà, ma di certo non si può contare) e quindi potrebbe non essere appropriato a seconda del contesto.
Un vantaggio nel farlo in questo modo è che osservando ovunque sia memorizzato un riferimento a quella stringa, è immediatamente ovvio da dove proviene; FOO_BAZ
è molto meno ambiguo di 2
.
Oltre a ciò, l'altra cosa che offende la mia sensibilità Pythonic per quanto riguarda la classe che proponi è l'uso di split ()
. Perché non passare semplicemente in una tupla, in un elenco o in altri enumerabili per cominciare?
Il modo integrato per fare enum è:
(FOO, BAR, BAZ) = range(3)
che funziona bene per piccoli set, ma presenta alcuni svantaggi:
- devi contare il numero di elementi a mano
- non puoi saltare valori
- se aggiungi un nome, devi anche aggiornare il numero dell'intervallo
Per un'implementazione enum completa in python, vedere: http://code.activestate.com/recipes/67107/
Ho iniziato con qualcosa che assomiglia molto alla risposta di S.Lott ma ho solo sovraccaricato 'str' ed 'eq' (invece dell'intera classe di oggetti) in modo da poter stampare e confrontare il valore dell'enum.
class enumSeason():
Spring = 0
Summer = 1
Fall = 2
Winter = 3
def __init__(self, Type):
self.value = Type
def __str__(self):
if self.value == enumSeason.Spring:
return 'Spring'
if self.value == enumSeason.Summer:
return 'Summer'
if self.value == enumSeason.Fall:
return 'Fall'
if self.value == enumSeason.Winter:
return 'Winter'
def __eq__(self,y):
return self.value==y.value
Stampa (x) produrrà il nome anziché il valore e due valori che contengono Primavera saranno uguali tra loro.
>>> x = enumSeason(enumSeason.Spring)
>>> print(x)
Spring
>>> y = enumSeason(enumSeason.Spring)
>>> x == y
True