A screencast overview of python enums as introduced by PEP 435

Sat 28 September 2013 | tags: python

Earlier in the week, I made my first screencast to give an overview of PEP 435 which introduces enums to the python standard library.


The code below is the set of python notes I had prepared as I made the screencast.

# What are enumerations ?
# A series of immutable discrete arbitrary values
# of some type
# may or may not have a semantic meaning

# Why need them as a separate construct ?

# You can represent them as constants with values eg. ints
# But what does GREEN * 2 mean ?
# And if GREEN = 2, when you print / log GREEN, you expect to see GREEN not 2
# And sometimes you want to treat all the values as a set eg. days of week

# PEP 435 is the formal python document introducing enums
# Implemented in 3.4. You will need a python 3.4 runtime

# How are enumerations constructed ?

from enum import Enum

# There are two ways to construct enums. The first is a functional API. eg.

Direction = Enum('Direction','North East South West')

# Direction is an enumeration
# It has 4 members


# Each member has a name and an associated value
# This is more apparent when you render the __repr__ of the member


# in this case the values were automatically provided.
# When values are automatically generated they are integers starting from 1

# You could also pass the member name and value as 2 tuples
StrDirection = Enum ('StrDirection',(("North", "N"),("East","E"), ("West", "W"), ("South", "S")))

# Or as a dict
AngleDirection = Enum ('AngleDirection',{"North": 90,"East":0, "West": 270, "South": 180})

# Note: while Direction is an iterable in definition order,
#       you can't pick up the zero'th element eg. Direction[0]
#       ie. you cannot index by position
# However Direction.North and Direction["North"] are both valid


# Curiously a enum can also be looked up using a value by using the call operator


# A key property of an enum is that its members are hashable

# Internal structure.
# Every Enum type has a __members__ attribute of the type OrderedDict


# enums are compared by identity and can be checked for equality
# however you cannot perform ordered comparisons

#   Aliases are not returned when iterating
# While two members cannot share the same name, they can share the same value (aliases)
# There is only one member that is shared in case of aliases

print(list(name for name, member in Colour.__members__.items() if member.name != name))

# An alternative way of defining an enum is a class
# Enum classes can have instance and classmethods

class Timezone:
    def __init__(self,offset,dst) :
        self.offset = offset
        self.dst = dst
    def earlier_than(self, other) :
        # ignoring dst
        return self.offset < other.offset
    def __eq__(self,other) :
        return self.offset == other.offset and self.dst == other.dst
    def __str__(self) :
        return "TZ({},{})".format(self.offset, self.dst)
    def __repr__(self) :
        return "TZ({},{})".format(self.offset, self.dst)

class Timezones(Enum):
    IST = Timezone(330, False)
    GMT = Timezone(0, False)
    UTC = Timezone(0, False)
    EST = Timezone(-300, False)
    def all_zones(cls):
        #   Aliases are not returned when iterating
        return list(n.name for n in cls)
    def aliases(cls):
        return [(name, member.name) for name, member in
                cls.__members__.items() if  member.name != name]
    def offset(self) :
        return  self.value.offset

for tz in Timezones :



Timezones.IST == Timezones.GMT

Timezones.GMT == Timezones.IST

Timezone.UTC < Timezone.IST

# An enum which does not have any members can be subclassed. One which has cannot be

# So far the values per se have not been important (except to distinguish between distinct / aliased members)
# However in some cases it is important, and for that a special case Enum ie. IntEnum is defined

from enum import IntEnum

class Education(IntEnum):
    Kindergarten = 1
    Primary = 2
    Secondary = 3
    HigherSecondary = 4
    Graduate = 5 # elsewhere eg. US, this would be under graduate
    PostGraduate = 6 # and this would be called graduate
    Doctorate = 7

# This is an example where the value (and its ordering particularly is considered important)
# Now operators relevant to the semantics of the mixed in type (in this case int) can be used

Education.Secondary > Education.Primary

# Note that this opens up semantic issues of equivalence across enum types. eg.

class Colour(IntEnum):
    Red = 1
    Green = 2
    Blue = 3

Education.Secondary == Colour.Blue

# Thus you can choose to bring in any value types consistent with their appropriate semantics
# Note that this could lead to unexpected results eg.


# However IntEnum is just a special case of being able to mix in values of any types ie.

class IntEnum(int, Enum) :

# So use value types that are semantically consistent