enum - Tipo Enumerazione
Scopo: Il modulo enum definisce un tipo enumerazione con capacità di iterazione e confronto.
Il modulo enum definisce un tipo enumerazione con capacità di iterazione e confronto. Può essere usato per creare simboli ben definiti per valori, invece di utilizzare interi letterali o stringhe.
Creare Enumerazioni
Una nuova enumerazione viene definita utilizzando la sintassi di class
subclassando Enum
ed aggiungendo attributi di classe che descrivono i valori.
# enum_create.py
import enum
class BugStatus(enum.Enum):
new = 7
incomplete = 6
invalid = 5
wont_fix = 4
in_progress = 3
fix_committed = 2
fix_released = 1
print('\nNome membro: {}'.format(BugStatus.wont_fix.name))
print('Nome membro: {}'.format(BugStatus.wont_fix.value))
I membri di Enum
sono convertiti in istanze mentre la classe viene elaborata. Ogni istanza ha una proprietà name
che corrisponde al nome del membro ed una proprietà value
che corrisponde al valore assegnato al nome nelle definizione della classe.
$ python3 enum_create.py Nome membro: wont_fix Nome membro: 4
Iterazione
L'iterazione sulla classe enum
produce i singoli membri dell'enumerazione.
# enum_iterate.py
import enum
class BugStatus(enum.Enum):
new = 7
incomplete = 6
invalid = 5
wont_fix = 4
in_progress = 3
fix_committed = 2
fix_released = 1
for status in BugStatus:
print('{:15} = {}'.format(status.name, status.value))
I membri sono prodotti nell'ordine nel quale sono stati dichiarati nelle definizione della classe. I nomi ed i valori non sono utilizzati in alcun modo per un ordinamento.
$ python3 enum_iterate.py new = 7 incomplete = 6 invalid = 5 wont_fix = 4 in_progress = 3 fix_committed = 2 fix_released = 1
Confrontare Enumerazioni
Poichè i membri delle enumerazioni non sono ordinati, supportano solo il confronto per identità ed uguaglianza.
# enum_comparison.py
import enum
class BugStatus(enum.Enum):
new = 7
incomplete = 6
invalid = 5
wont_fix = 4
in_progress = 3
fix_committed = 2
fix_released = 1
actual_state = BugStatus.wont_fix
desired_state = BugStatus.fix_released
print('Uguaglianza:',
actual_state == desired_state,
actual_state == BugStatus.wont_fix)
print('Identità :',
actual_state is desired_state,
actual_state is BugStatus.wont_fix)
print('Ordinati per valore:')
try:
print('\n'.join(' ' + s.name for s in sorted(BugStatus)))
except TypeError as err:
print(' Non ordinabili: {}'.format(err))
Gli operatori di confronto maggiore-di e minore-di sollevano una eccezione TypeError
.
$ python3 enum_comparison.py Uguaglianza: False True Identità : False True Ordinati per valore: Non ordinabili: unorderable types: BugStatus() < BugStatus()
Si utilizzi la classe IntEnum
per enumerazioni dove i membri devono avere un comportamento che più si avvicini ai numeri, ad esempio per supportare confronti.
# enum_intenum.py
import enum
class BugStatus(enum.IntEnum):
new = 7
incomplete = 6
invalid = 5
wont_fix = 4
in_progress = 3
fix_committed = 2
fix_released = 1
print('Ordinati per valore:')
print('\n'.join(' ' + s.name for s in sorted(BugStatus)))
$ python3 enum_intenum.py Ordinati per valore: fix_released fix_committed in_progress wont_fix invalid incomplete new
Valori di Enumerazione Univoci
I membri di Enum
con lo stesso valore sono trattati come riferimenti alias allo stesso oggetto membro. Un qualsiasi alias non fa sì che siano presenti valori ripetuti nell'iteratore per Enum
.
# enum_aliases.py
import enum
class BugStatus(enum.Enum):
new = 7
incomplete = 6
invalid = 5
wont_fix = 4
in_progress = 3
fix_committed = 2
fix_released = 1
by_design = 4
closed = 1
for status in BugStatus:
print('{:15} = {}'.format(status.name, status.value))
print('\nUguale: by_design è wont_fix: ',
BugStatus.by_design is BugStatus.wont_fix)
print('Uguale: closed è fix_released: ',
BugStatus.closed is BugStatus.fix_released)
Visto che by_design
e closed
sono alias per altri membri, essi non appaiono separatamente nell'output quando si itera attraverso Enum
. IL nome canonico per un membro è il primo nome abbinato al valore.
$ python3 enum_aliases.py new = 7 incomplete = 6 invalid = 5 wont_fix = 4 in_progress = 3 fix_committed = 2 fix_released = 1 Uguale: by_desing è wont_fix: True Uguale: closed è fix_released: True
Per richiedere che tutti i membri abbiano valori univoci, si aggiunge il decoratore @unique
ad Enum
.
# enum_unique_enforce.py
import enum
@enum.unique
class BugStatus(enum.Enum):
new = 7
incomplete = 6
invalid = 5
wont_fix = 4
in_progress = 3
fix_committed = 2
fix_released = 1
# Questo solleverà un errore quanto si applica unique
by_design = 4
closed = 1
I membri con valori ripetuti scatenano una eccezione ValueError
quando la classe Enum
viene interpretata.
$ python3 enum_unique_enforce.py Traceback (most recent call last): File "enum_unique_enforce.py", line 7, in <module> class BugStatus(enum.Enum): File "/usr/lib/python3.4/enum.py", line 555, in unique (enumeration, alias_details)) ValueError: duplicate values found in <enum 'BugStatus'>: by_design -> wont_fix, closed -> fix_released
Creare Programmaticamente Enumerazioni
Ci sono casi in cui è più vantaggioso creare enumerazioni programmaticamente, piuttosto che scriverle direttamente nel codice in una definizione di classe. Per queste situazioni, Enum
supporta anche il passaggio di nomi e valori di membro alla classe costruttore.
# enum_programmatic_create.py
import enum
BugStatus = enum.Enum(
value='BugStatus',
names=('fix_released fix_committed in_progress '
'wont_fix invalid incomplete new'),
)
print('Membri: {}'.format(BugStatus.new))
print('\nTutti i membri:')
for status in BugStatus:
print('{:15} = {}'.format(status.name, status.value))
L'argomento value
è il nome dell'enumerazione, utilizzato per costruire la rappresentazione dei membri. L'argomento names
elenca i membri dell'enumerazione. Quando viene passata una singola stringa, viene divisa su spazi e virgole ed i token sono utilizzati come nomi per i membri, ai quali viene automaticamente assegnato un valore a partire da 1.
$ python3 enum_programmatic_create.py Membri: BugStatus.new Tutti i membri: fix_released = 1 fix_committed = 2 in_progress = 3 wont_fix = 4 invalid = 5 incomplete = 6 new = 7
Per un maggiore controllo sui valori associati ai membri, la stringa names
può essere sostituita da un a sequenza di tuple da due elementi o da un dizionario che mappi nomi ai valori.
import enum
BugStatus = enum.Enum(
value='BugStatus',
names=[
('new', 7),
('incomplete', 6),
('invalid', 5),
('wont_fix', 4),
('in_progress', 3),
('fix_committed', 2),
('fix_released', 1),
],
)
print('Tutti i membri:')
for status in BugStatus:
print('{:15} = {}'.format(status.name, status.value))
IN questo esempio viene passata un lista di tuple da due elementi in luogo di una singola stringa che contiene solo i nomi dei membri. Questo rende possibile ricostruire l'enumerazione BugStatus
con i membri nello stesso ordine della versione creata nell'esempio enum_create.py
.
$ python3 enum_programmatic_mapping.py Tutti i membri: new = 7 incomplete = 6 invalid = 5 wont_fix = 4 in_progress = 3 fix_committed = 2 fix_released = 1
Valori Membri Non Interi
I valori membri di Enum non sono limitati ad interi. Qualsiasi tipo di oggetto può essere associato ad un membro. Se il valore è una tupla, i membri sono passati come argomenti singoli ad __init__()
. Altri oggetti sono passati direttamente ad __init__()
come solo argomento oltre a self
.
# enum_complex_values.py
import enum
class BugStatus(enum.Enum):
new = {
'value': 7,
'transitions': ['incomplete',
'invalid',
'wont_fix',
'in_progress',
],
}
incomplete = {
'value': 6,
'transitions': ['new', 'wont_fix'],
}
invalid = {
'value': 5,
'transitions': ['new'],
}
wont_fix = {
'value': 4,
'transitions': ['new'],
}
in_progress = {
'value': 3,
'transitions': ['new', 'fix_committed'],
}
fix_committed = {
'value': 2,
'transitions': ['in_progress', 'fix_released'],
}
fix_released = {
'value': 1,
'transitions': ['new'],
}
def __init__(self, vals):
self.num = vals['value']
self.transitions = vals['transitions']
def can_transition(self, new_state):
return new_state.name in self.transitions
print('Nome:', BugStatus.in_progress)
print('Valore:', BugStatus.in_progress.value)
print('Attributo personalizzato:', BugStatus.in_progress.transitions)
print('Attributo in uso:',
BugStatus.in_progress.can_transition(BugStatus.new))
In questo esempio ogni valore di membro è un dizionario che contiene il valore numerico (come sarebbe potuto essere salvato in un database) ed una lista di transizioni valide diverse dello stato corrente.
$ python3 enum_complex_values.py Nome: BugStatus.in_progress Valore: {'transitions': ['new', 'fix_committed'], 'value': 3} Attributo personalizzato: ['new', 'fix_committed'] Attributo in uso: True
Vedere anche:
- Enum
- La documentazione della libreria standard per questo modulo.
- PEP 435
- Aggiungere un tipo Enum alla libreria standard di Python
- flufl.enum
- La fonte ispiratrice originale per enum di Barry Warsaw.