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 argumentname
string is the class name. The second argumentbases
tuple specifies the base classes of the new type. The third argumentdict
is the namespace containing definitions for class body.
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, anenum
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.