How to use Python Enums.

In a Nutshell

Here are a few quick examples of how to use Enums in Python.

Create an EntityType enum and get a list of its members thus:

>>> from enum import Enum
>>> EntityType = Enum('EntityType', ['Person', 'Organization', 'Location'])
>>> list(EntityType)
[<EntityType.Person: 1>, <EntityType.Organization: 2>, <EntityType.Location: 3>]

If for some reason you want the values to begin at 0, use the start parameter:

>>> EntityType = Enum('EntityType', ['Person', 'Organization', 'Location'], start=0)
>>> list(EntityType)
[<EntityType.Person: 0>, <EntityType.Organization: 1>, <EntityType.Location: 2>]

Use the class keyword to create an enum with more complicated properties like methods.

>>> from enum import Enum
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3
...

Get a string representation of an Enum member by using its name:

>>> EntityType.Person.name
'Person'

Or use str() to get a fuller name:

>>> str(EntityType.Person)
'EntityType.Person'

Use square brackets to create an enum member from a string:

>>> enumObject = EntityType['Person']
>>> enumObject
<EntityType.Person: 1>

To test whether a string is a valid enum member, you have two options.

  • Option 1 is to try to create an enum object from it, and handle the exception.
>>> enumObject = EntityType['Pearson']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.5/enum.py", line 277, in __getitem__
    return cls._member_map_[name]
KeyError: 'Pearson'
  • Option 2 is to use list comprehension to get a list of each enum attribute's 'name' property, and then test whether your string is in that list.
>>> 'Pearson' in [x.name for x in list(EntityType)]
False

>>> 'Person' in [x.name for x in list(EntityType)]
True

Use isinstance() to assert that an object is a specific type of an enum:

>>> enumObject = EntityType.Organization
>>> isinstance(enumObject, EntityType)

Use is or == to assert that an enum object is a particular enum member:

>>> enumObject = EntityType.Location

>>> enumObject is EntityType.Person
False

>>> enumObject is EntityType.Location
True

>>> enumObject == EntityType.Location
True

Here's how you iterate through all the members of an enum:

>>> members = []
>>> for item in EntityType:
...     members.append(item.name)
... 
>>> members
['Person', 'Organization', 'Location']

Details

I still have much to learn about being "pythonic," so perhaps here I'm only revealing my ignorance. But it seems to me that too much Python code uses strings when enums are really what is called for.

Enums are superior to strings in at least two ways:

  • They provide automatic error checking. Its easy to to type "Yallow" when you mean "Yellow," or "Febuary" when you mean "February." Enums save you from making those kinds of mistakes.
  • If you decide to rename an enum member, you'll save yourself much time and possibly errors by using an enum instead of a string. Especially if you use an IDE and it has refactoring functionality—in which case it's a breeze.

Nomenclature

The Python enum documentation provides a much-needed guide to enum nomenclature. Here I borrow heavily from the documentation—at points even crossing into outright plagiarism.

>>> from enum import Enum
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3
...

In the example above:

  • Color is an enum or enumeration.
  • Color.RED is an attribute.
  • The attributes Color.RED and Color.GREEN are enum members (or enumeration members).
  • Each enum member has a name and a value. The name of Color.RED is RED, and its value is 1.

Get enum from value

Sometimes enum values are meaningless. But sometimes they are meaningful in the sense that enumeration members can be greater or less than one another.

>>> Quality = Enum('Quality', ['VeryLow', 'Low', 'Medium', 'High', 'VeryHigh'])

>>> Quality.Low.value < Quality.VeryHigh.value
True

>>> Quality.Medium.value > Quality.High.value
False

Let's say that you have an object that has a Quality component.

>>> class Car(object):
...     def __init__(self, quality):
...             self.quality = quality
... 

>>> myCar = Car(Quality.VeryHigh)

>>> myCar.quality
<Quality.VeryHigh: 5>

Suppose now that you want to reduce that object's quality by two degrees. In other words, if its Quality is VeryHigh, you want to change the Quality to Medium; and if Medium, you want to set it to VeryLow. To do this you will need to be able to set the quality using the enum's value. You do this in the following manner:

>>> myCar.quality = Quality(myCar.quality.value - 2)

>>> myCar.quality
<Quality.Medium: 3>

External Resources

Check out http://xion.io/post/code/python-enums-are-ok.html for a good posting about enums in Python.

A Regret

I can't leave the topic of enums without expressing this regret: I would really like auto-complete to work for enums in Python, as it does in Java. Alas, it doesn't seem to—neither in Eclipse's PyDev nor in JetBrains' PyCharm.