Python Enums

by John | May 14, 2023

 

Enumerations are a data type to define a related set of immutable values. In this article we will discuss how to create enums and how we can extend them. We will also give some examples of where they could be used in real applications. 

Enums are a little difficult to explain in words, so let's jump right in with some simple examples. Enums are quite similar to named tuples, so let's refresh on what they are and compare them to enums.

 

from collections import namedtuple

Color = namedtuple('Color', 'name value')

red = Color('red', 1)
green = Color('green', 2)
blue = Color('blue', 3)

print(red.name, red.value)
print(green.name, green.value)
print(blue.name, blue.value)

#red 1
#green 2
#blue 3

 

To rewrite named tuples above in an enum we can use the following syntax. Notice we must import Enum from the built in library enum.

 

from enum import Enum
class Color(Enum):
    red = 1
    green = 2
    blue = 3

print(Color.red)    
#Color.red
print(Color.red.name, Color.red.value)
#red 1

 

Enumerations are Iterables

We can loop over an enum as follows:

 

for c in Color: print(c)
#Color.red
#Color.green
#Color.blue

 

Enumerations do not Support Reassignment

We can't modify the values in an enumeration. That would sort of defeat the purpose of creating one anyway!

 

class Color(Enum):
    red = 1
    green = 2
    blue = 3
try:   
    Color.red = 10
except Exception as ex:
    print(ex)

#Cannot reassign members.

 

So that is fairly straightforward although I am sure you will agree it seems pretty useless and you may be searching for a potential use case in your own projects. Well, let's stick with the color theme and make a enum that better illustrates how you can use these data types.

 

Examples

Say you are a front-end engineer and you want to store the values of hex colors in a class where you can easily access them by name. For those who don't know what a hex is, it is basically a color for displaying colors on the web.. For example black = '#000000. These values are related but clearly the human readable name is much easier for us to understand and we don't want to have to check these values every time we want to use a hex color.

 

class Blues(Enum):
     AliceBlue = '#F0F8FF'
     Aqua = '#00FFFF' 
     Aquamarine = '#7FFFD4'
     MediumBlue = '#0000CD'
     MidnightBlue = '#191970'
     Navy = '#000080'
     RoyalBlue = '#4169E1'
     SteelBlue = '#4682B4'

 

Adding Methods

We can also add methods to enums as they are just like any other class. Let's add a class method that shows a sample of the various types of blue we defined in Hex colors above. For this example we will use matplotlib, if you don't have this already installed and want to follow along you can just pip install matplotlib. 

 

import matplotlib.pyplot as plt

class Blues(Enum):
     AliceBlue = '#F0F8FF'
     Aqua = '#00FFFF' 
     Aquamarine = '#7FFFD4'
     MediumBlue = '#0000CD'
     MidnightBlue = '#191970'
     Navy = '#000080'
     RoyalBlue = '#4169E1'
     SteelBlue = '#4682B4'
     
     @classmethod
     def show_sample(cls):
         plt.figure(figsize=(10,4))
         for c in list(Blues):
             print(c.name, c.value)
             plt.bar(c.name, 5, color=c.value)
         plt.tight_layout()


Blues.show_sample()

#AliceBlue #F0F8FF
#Aqua #00FFFF
#Aquamarine #7FFFD4
#MediumBlue #0000CD
#MidnightBlue #191970
#Navy #000080
#RoyalBlue #4169E1
#SteelBlue #4682B4

 

 

 

 

Let's take another quick example, regarding HTTP errors. Say for example you are designing an API and want to contain the response codes in an object. This could be useful as it makes errors less likely! Since the responses below are each unique, we can ensure there is no duplication using a decorator from the enum module. 

 

from enum import unique

@unique
class ClientErrorResponses(Enum):
    BadRequest = 400
    Unauthorized = 401
    PaymentRequired = 402
    Forbidden = 403
    NotFound = 404
    MethodNotAllowed = 405
    NotAcceptable = 406
    ProxyAuthRequired = 407
    RequestTimeout = 408
    Conflict = 409
    Gone = 410
    LengthRequired = 411
    PreconditionFailed = 412
    PayLoadTooLarge = 413
  

 

If we were to try to add another error with one of the codes displayed above we would get an AttributeError. Python actually has a built in module for this exact task and they use enumerations! 

 

from http import HTTPStatus

for status in HTTPStatus:
    print(status.name, status.value)

 

Below we show a sample for the client server errors. 

Out:
BAD_REQUEST 400
UNAUTHORIZED 401
PAYMENT_REQUIRED 402
FORBIDDEN 403
NOT_FOUND 404
METHOD_NOT_ALLOWED 405
NOT_ACCEPTABLE 406
PROXY_AUTHENTICATION_REQUIRED 407
REQUEST_TIMEOUT 408
CONFLICT 409
GONE 410
LENGTH_REQUIRED 411
PRECONDITION_FAILED 412
REQUEST_ENTITY_TOO_LARGE 413
REQUEST_URI_TOO_LONG 414
UNSUPPORTED_MEDIA_TYPE 415
REQUESTED_RANGE_NOT_SATISFIABLE 416
EXPECTATION_FAILED 417
MISDIRECTED_REQUEST 421
UNPROCESSABLE_ENTITY 422
LOCKED 423
FAILED_DEPENDENCY 424
UPGRADE_REQUIRED 426
PRECONDITION_REQUIRED 428
TOO_MANY_REQUESTS 429
REQUEST_HEADER_FIELDS_TOO_LARGE 431

 

 

Extending Enums

We need to be careful when trying to extend enums. Take the following example which raises a TypeError.

 

class Color(Enum):
    red = 1
    green = 2
    blue = 3

class MoreColors(Color):
    brown = 5


#TypeError: Cannot extend enumerations

 

However the code below will work. The code below works as we don't declare any members within the parent enum. The rationale for allowing this functionality from the Python docs. 

"it makes sense to allow sharing some common behavior between a group of enumerations"

 

So we can create methods in the parent class that we may need in the child classes. We will give an example of this regarding the hex color enum class we created previously. 

 

class Color(Enum):
    def my_func(self):
        pass

class MoreColors(BaseColor):
    red = 1 
    blue = 2 
    
class YetMoreColors(BaseColor):
    yellow = 2
   

 

In the context of our hex color class we created, we can create a base class which implements 2 class methods, we can then create subclasses that inherit these methods. 

 

class Color(Enum):
    @classmethod
    def list_colors(cls):
        for c in list(cls):
            print(c.name, c.value)
    
    @classmethod
    def show_sample(cls):
        plt.figure(figsize=(9,2))
        for c in list(cls):
            plt.bar(c.name, 5, color=c.value)
        plt.tight_layout()
        plt.show()
            
        
class Blues(Color):
     AliceBlue = '#F0F8FF'
     Aqua = '#00FFFF' 
     Aquamarine = '#7FFFD4'
     MediumBlue = '#0000CD'
     MidnightBlue = '#191970'
     Navy = '#000080'
     RoyalBlue = '#4169E1'
     SteelBlue = '#4682B4'


class Reds(Color):
    Crimson = '#DC143C'
    DarkRed = '#8B0000'
    FireBrick = '#B22222'
    

class Greens(Color):
    Green = '#008000'
    ForestGreen = '#228B22'
    LawnGreen = '#7CFC00'
    

Greens.show_sample()

Reds.list_colors()
#Crimson #DC143C
#DarkRed #8B0000
#FireBrick #B22222

 

 

 

Hopefully that gives you a good introduction to using enumerations in Python. Take a look at the Python documentation for more technical details. 

 

 

 


Join the discussion

Share this post with your friends!