"
This article is part of in the series
Last Updated: Wednesday 29th December 2021

What is enum and why we need it?

An enumerated type, a.k.a. enum, is a data type consisting of a set of named values called elements, members or enumerators of the type. These enumerated named values function as constants in the computing language. For example, a COLOR enum may include named values such as RED, GREEN and BLUE. Notice all the named values are written in capital form to distinguish them as constants, which are significantly different from variables.

So, why do we need to use an enum? Imagine a scenario where you may want to limit the type of gender for users in a website to MALE, FEMALE and N/A. Of course, a list of strings will do the job just fine like user.gender = 'MALE' or user.gender = 'FEMALE'. However, using a string as a value for the gender attribute is not robust to programmer errors and malicious attacks. A programmer could easily mistype 'MALE' to 'ALE' or 'FEMALE' to 'EMAIL' and the code would still run and produce funny but terrible results. An attacker may provide carefully constructed garbage string values to the system, trying to cause a crash or gain root access. If we use an enum to restrict the user.gender attribute to a limited list of values, none of the aforementioned problems will happen.

Python enum the easy way

In Python, the built-in function type accepts one or three arguments. According to the Python documentation, When you pass one argument to type, it returns the type of an object. When you pass three arguments to type as in type(name, bases, dict):

It returns a new type object. This is essentially a dynamic form of the class statement. The first argument name string is the class name. The second argument bases tuple specifies the base classes of the new type. The third argument dict is the namespace containing definitions for class body.

http://docs.python.org/3/library/functions.html#type

Using type, we can construct an enum in the following way:

[python]
>>> def enum(**named_values):
... return type('Enum', (), named_values)
...
>>> Color = enum(RED='red', GREEN='green', BLUE='blue')
>>> Color.RED
'red'
>>> Color.GREEN
'green'
>>> Gender = enum(MALE='male', FEMALE='female', N_A='n/a')
>>> Gender.N_A
'n/a'
>>> Gender.MALE
'male'
[/python]

Now Color and Gender are classes that behaves like enum and you could use them in the following way:

[python]
>>> class User(object):
... def __init__(self, gender):
... if gender not in (Gender.MALE, Gender.FEMALE, Gender.N_A):
... raise ValueError('gender not valid')
... self.gender = gender
...
>>> u = User('malicious string')
Traceback (most recent call last):
File "", line 1, in
File "", line 4, in __init__
ValueError: gender not valid
[/python]

Notice the invalid string passed-in gets rejected by the constructor of User.

Python enum the fancy way

Although the previous way of implementing enum is easy, it does require you to explicitly specify a value for each named value. For example, in enum(MALE='male', FEMALE='female', N_A='n/a'), MALE is a the name and 'male' is the value for that name. Since most of the time we only use enum by its names, we can implement an enum that does automatic value assignment in the following way:

[python]
>>> def enum(*args):
... enums = dict(zip(args, range(len(args))))
... return type('Enum', (), enums)
...
>>> Gender = enum('MALE', 'FEMALE', 'N_A')
>>> Gender.N_A
2
>>> Gender.MALE
0
[/python]

Using zip and range, the code automatically assigns an integer value to each name in args.

Tips and Suggestions for Python enums

  • An enum helps limit the exposed choices of values for an attribute, which could be very useful in real-world programming. When dealing with databases, an enum that is identical across Python and the database management system could prevent lots of hard-to-find bugs.
  • Using an enum to safe guard your system against malicious input. Always check the input values before accepting them into your system.

About The Author