Creating Enumerations

A new enumeration is defined using the class syntax by subclassing Enum and adding class attributes describing the values


In [ ]:
import enum

In [ ]:
class BugStatus(enum.Enum):
    new = 7
    incomplete = 6
    invalid = 5
    wont_fix = 4
    in_progress = 3
    fix_committed = 2
    fix_released = 1

In [ ]:
print("member name: {}".format(BugStatus.wont_fix.name))
print("member value: {}".format(BugStatus.wont_fix.value))

Access to enumeration members and their attributes


In [ ]:
BugStatus(7)

In [ ]:
# if you want access enum members by name, use item access

In [ ]:
BugStatus['']

Iteration

Iterating over the enum class pruduces the indivisual members of the member


In [ ]:
for status in BugStatus:
    print("{}:{}".format(status.name, status.value))

Note: The members are produced in the order they are declared in the class definition. The names and values are not used to sort them.

Comparing Enums

Because enumeration members are not ordered. They only support equality and identify test


In [ ]:
actual_state = BugStatus.wont_fix
desired_state = BugStatus.fix_released

print('Equality:',
      actual_state == desired_state,
      actual_state == BugStatus.wont_fix)
print('Identity:',
      actual_state is desired_state,
      actual_state is BugStatus.wont_fix)
print('Ordered by value:')
try:
    print('\n'.join('  ' + s.name for s in sorted(BugStatus)))
except TypeError as err:
    print('  Cannot sort: {}'.format(err))

IntEnum

use the IntEnum class for enumrations where the members need to behave more like numbers, for example, to support comparisons


In [ ]:
class BugStatusInt(enum.IntEnum):

    new = 7
    incomplete = 6
    invalid = 5
    wont_fix = 4
    in_progress = 3
    fix_committed = 2
    fix_released = 1

In [ ]:
actual_state = BugStatusInt.wont_fix
desired_state = BugStatusInt.fix_released

print('Equality:',
      actual_state == desired_state,
      actual_state == BugStatusInt.wont_fix)
print('Identity:',
      actual_state is desired_state,
      actual_state is BugStatusInt.wont_fix)

print("comparison: ")
print("new is bigger then invalid:", BugStatusInt.new > BugStatusInt.invalid)

print('Ordered by value:')
print('\n'.join('  ' + s.name for s in sorted(BugStatusInt)))

Unique Enumeration Values

Enum members with the same value are tracked as alias references to the same member object. Aliases do not cause repeated values to be present in the iterator for the Enum


In [ ]:
class BugStatusUnique(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 BugStatusUnique:
    print('{:15} = {}'.format(status.name, status.value))

print('\nSame: by_design is wont_fix: ',
      BugStatusUnique.by_design is BugStatusUnique.wont_fix)
print('Same: closed is fix_released: ',
      BugStatusUnique.closed is BugStatusUnique.fix_released)

To require all members to have unique values, add the decorator


In [ ]:
@enum.unique
class BugStatusUniqueDecorator(enum.Enum):

    new = 7
    incomplete = 6
    invalid = 5
    wont_fix = 4
    in_progress = 3
    fix_committed = 2
    fix_released = 1

    # This will trigger an error with unique applied.
    by_design = 4
    closed = 1

Members with repeated values trigger a ValueError exception when the Enum class being interpreted

Create Enumerations Programmatically


In [ ]:
BugStatus = enum.Enum(
    value='BugStatus',
    names=('fix_released fix_committed in_progress '
           'wont_fix invalid incomplete new'),
)

print('Member: {}'.format(BugStatus.new))

print('\nAll members:')
for status in BugStatus:
    print('{:15} = {}'.format(status.name, status.value))

value: the name of the enumration names: lists the member of the enumeration, if a string is passed. it is split on the whitespace and commas. the value to names is starting with 1

If you want control the value associated with members, the names string can be replaced with a sequence of 2 part tuples or a dictionary mapping names to values


In [ ]:
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('All members:')
for status in BugStatus:
    print('{:15} = {}'.format(status.name, status.value))

Non-integer Member Values

Enum member values are not restricted to integers. In fact, any type of object can be associated with a member. If the value is a tuple, the members are passed as individual arguments to init().


In [ ]:
class BugStatus(enum.Enum):

    new = (7, ['incomplete',
               'invalid',
               'wont_fix',
               'in_progress'])
    incomplete = (6, ['new', 'wont_fix'])
    invalid = (5, ['new'])
    wont_fix = (4, ['new'])
    in_progress = (3, ['new', 'fix_committed'])
    fix_committed = (2, ['in_progress', 'fix_released'])
    fix_released = (1, ['new'])

    def __init__(self, num, transitions):
        self.num = num
        self.transitions = transitions

    def can_transition(self, new_state):
        return new_state.name in self.transitions


print('Name:', BugStatus.in_progress)
print('Value:', BugStatus.in_progress.value)
print('Custom attribute:', BugStatus.in_progress.transitions)
print('Using attribute:',
      BugStatus.in_progress.can_transition(BugStatus.new))

In [ ]:
BugStatus.new.transitions

For more complex cases, tuples might become unwieldy. Since member values can be any type of object, dictionaries can be used for cases where there are a lot of separate attributes to track for each enum value. Complex values are passed directly to init() as the only argument other than self.


In [ ]:
import enum


class BugStatus(enum.Enum):

    new = {
        'num': 7,
        'transitions': [
            'incomplete',
            'invalid',
            'wont_fix',
            'in_progress',
        ],
    }
    incomplete = {
        'num': 6,
        'transitions': ['new', 'wont_fix'],
    }
    invalid = {
        'num': 5,
        'transitions': ['new'],
    }
    wont_fix = {
        'num': 4,
        'transitions': ['new'],
    }
    in_progress = {
        'num': 3,
        'transitions': ['new', 'fix_committed'],
    }
    fix_committed = {
        'num': 2,
        'transitions': ['in_progress', 'fix_released'],
    }
    fix_released = {
        'num': 1,
        'transitions': ['new'],
    }

    def __init__(self, vals):
        self.num = vals['num']
        self.transitions = vals['transitions']

    def can_transition(self, new_state):
        return new_state.name in self.transitions
    
print('Name:', BugStatus.in_progress)
print('Value:', BugStatus.in_progress.value)
print('Custom attribute:', BugStatus.in_progress.transitions)
print('Using attribute:',
      BugStatus.in_progress.can_transition(BugStatus.new))

In [ ]: