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.