Basics Intermediate Advanced
aialgorithmsapibest-practicescareercommunitydatabasesdata-sciencedata-structuresdata-vizdevopsdjangodockereditorsflaskfront-endgamedevguimachine-learningnewsnumpyprojectspythonstdlibtestingtoolsweb-devweb-scraping
Recommended Course

Building Enumerations With Python's enum
59m · 13 lessons

Build Enumerations of Constants With Python's Enum
Table of Contents
Recommended Course
Python’senum module offers a way to create enumerations, a data type allowing you to group related constants. You can define an enumeration using theEnum class, either by subclassing it or using its functional API. This tutorial will guide you through the process of creating and using Python enums, comparing them to simple constants, and exploring specialized types likeIntEnum,IntFlag, andFlag.
By the end of this tutorial, you’ll understand that:
- Anenum in Pythongroups related constants in a single data type using the
Enumclass. - Youcreate enumerations bysubclassing
Enumor using the module’sfunctional API. - Using
Enumover simple constantsprovides structure,prevents reassignment, andenhances code readability. Enum,IntEnum,IntFlag, andFlagdiffer in their support forinteger operations andbitwise flags.- Enums can work with data types likeintegers orstrings, boosting their flexibility.
To follow along with this tutorial, you should be familiar withobject-oriented programming andinheritance in Python.
Get Your Code:Click here to download the free sample code that you’ll use to build enumerations in Python.
Getting to Know Enumerations in Python
Several programming languages, includingJava andC++, have a nativeenumeration orenum data type as part of their syntax. This data type allows you to create sets ofnamed constants, which are consideredmembers of the containing enum. You can access the members through the enumeration itself.
Enumerations come in handy when you need to define animmutable anddiscrete set of similar or related constant values that may or may not have semantic meaning in your code.
Days of the week, months and seasons of the year, Earth’s cardinal directions, a program’s status codes, HTTP status codes, colors in a traffic light, and pricing plans of a web service are all great examples of enumerations in programming. In general, you can use an enum whenever you have a variable that can take one of alimited set of possible values.
Python doesn’t have an enum data type as part of its syntax. Fortunately, Python 3.4 added theenum module to thestandard library. This module provides theEnum class for supporting general-purpose enumerations in Python.
Enumerations were introduced by PEP 435, which defines them as follows:
An enumeration is a set of symbolic names bound to unique, constant values. Within an enumeration, the values can be compared by identity, and the enumeration itself can be iterated over. (Source)
Before this addition to the standard library, you could create something similar to an enumeration by defining a sequence of similar or related constants. To this end, Python developers often used the following idiom:
>>>RED,GREEN,YELLOW=range(3)>>>RED0>>>GREEN1Even though this idiom works, it doesn’t scale well when you’re trying to group a large number of related constants. Another inconvenience is that the first constant will have a value of0, which is falsy in Python. This can be an issue in certain situations, especially those involvingBoolean tests.
Note: If you’re using a Python version before 3.4, then you can create enumerations by installing theenum34 library, which is a backport of the standard-libraryenum. Theaenum third-party library could be an option for you as well.
In most cases, enumerations can help you avoid the drawbacks of the above idiom. They’ll also help you produce more organized, readable, and robust code. Enumerations have several benefits, some of which relate to ease of coding:
- Allowing for convenientlygrouping related constants in a sort ofnamespace
- Allowing foradditional behavior with custom methods that operate on either enum members or the enum itself
- Providing quick and flexibleaccess to enum members
- Enablingdirect iteration over members, including their names and values
- Facilitatingcode completion withinIDEs and editors
- Enablingtype anderror checking with static checkers
- Providing a hub ofsearchable names
- Mitigatingspelling mistakes when using the members of an enumeration
They also make your code robust by providing the following benefits:
- Ensuringconstant values that can’t be changed during the code’s execution
- Guaranteeingtype safety by differentiating the same value shared across several enums
- Improvingreadability andmaintainability by using descriptive names instead of mysterious values ormagic numbers
- Facilitatingdebugging by taking advantage of readable names instead of values with no explicit meaning
- Providing asingle source of truth andconsistency throughout the code
Now that you know the basics of enumerations in programming and in Python, you can start creating your own enum types by using Python’sEnum class.
Creating Enumerations With Python’sEnum
Python’senum module provides theEnumclass, which allows you to create enumeration types. To create your own enumerations, you can either subclassEnum or use its functional API. Both options will let you define a set of related constants as enum members.
In the following sections, you’ll learn how to create enumerations in your code using theEnum class. You’ll also learn how to set automatically generated values for your enums and how to create enumerations containing alias and unique values. To kick things off, you’ll start by learning how to create an enumeration by subclassingEnum.
Creating Enumerations by SubclassingEnum
Theenum module defines a general-purpose enumeration type withiteration andcomparison capabilities. You can use this type to create sets of named constants that you can use to replace literals of common data types, such as numbers and strings.
A classic example of when you should use an enumeration is when you need to create a set of enumerated constants representing the days of the week. Each day will have a symbolic name and a numeric value between1 and7, inclusive.
Here’s how you can create this enumeration by usingEnum as yoursuperclass orparent class:
>>>fromenumimportEnum>>>classDay(Enum):...MONDAY=1...TUESDAY=2...WEDNESDAY=3...THURSDAY=4...FRIDAY=5...SATURDAY=6...SUNDAY=7...>>>list(Day)[ <Day.MONDAY: 1>, <Day.TUESDAY: 2>, <Day.WEDNESDAY: 3>, <Day.THURSDAY: 4>, <Day.FRIDAY: 5>, <Day.SATURDAY: 6>, <Day.SUNDAY: 7>]YourDay class is a subclass ofEnum. So, you can callDay anenumeration, or just anenum.Day.MONDAY,Day.TUESDAY, and the like areenumeration members, also known asenum members, or justmembers. Each member must have avalue, which needs to be constant.
Because enumeration members must be constants, Python doesn’t allow you to assign new values to enum members at runtime:
>>>Day.MONDAY=0Traceback (most recent call last):...AttributeError:Cannot reassign members.>>>Day<enum 'Day'>>>># Rebind Day>>>Day="Monday">>>Day'Monday'If you try to change the value of an enum member, then you get anAttributeError. Unlike member names, the name containing the enumeration itself isn’t a constant but a variable. So, it’s possible to rebind this name at any moment during your program’s execution, but you should avoid doing that.
In the example above, you’ve reassignedDay, which now holds a string rather than the original enumeration. By doing this, you’ve lost the reference to the enum itself.
Often, the values mapped to members are consecutive integer numbers. However, they can be of any type, including user-defined types. In this example, the value ofDay.MONDAY is1, the value ofDay.TUESDAY is2, and so on.
Note: You may have noticed that the members ofDay are capitalized. Here’s why:
Because Enums are used to represent constants we recommend using UPPER_CASE names for enum members… (Source)
You can think of enumerations as collections of constants. Likelists,tuples, ordictionaries, Python enumerations are also iterable. That’s why you can uselist() to turn an enumeration into alist of enumeration members.
The members of a Python enumeration are instances of the container enumeration itself:
>>>fromenumimportEnum>>>classDay(Enum):...MONDAY=1...TUESDAY=2...WEDNESDAY=3...THURSDAY=4...FRIDAY=5...SATURDAY=6...SUNDAY=7...>>>type(Day.MONDAY)<enum 'Day'>>>>type(Day.TUESDAY)<enum 'Day'>You shouldn’t confuse a custom enum class likeDay with its members:Day.MONDAY,Day.TUESDAY, and so on. In this example, theDay enum type is a hub for enumeration members, which happen to be of typeDay.
You can also use the idiom based onrange() to build enumerations:
>>>fromenumimportEnum>>>classSeason(Enum):...WINTER,SPRING,SUMMER,FALL=range(1,5)...>>>list(Season)[ <Season.WINTER: 1>, <Season.SPRING: 2>, <Season.SUMMER: 3>, <Season.FALL: 4>]In this example, you userange() with thestart andstop arguments. Thestart argument allows you to provide the number that starts the range, while thestop argument defines the number at which the range will stop generating numbers.
Even though you use theclass syntax to create enumerations, they’re special classes that differ from normal Python classes. Unlike regular classes, enums:
- Can’t beinstantiated
- Can’t besubclassed unless the base enum has no members
- Provide a human-readablestring representation for their members
- Areiterable, returning their members in a sequence
- Providehashable members that can be used asdictionary keys
- Support thesquare bracket syntax,call syntax, anddot notation to access members
- Don’t allow memberreassignments
You should keep in mind all these subtle differences when you start creating and working with your own enumerations in Python.
Often, the members of an enumeration take consecutive integer values. However, in Python, the values of members can be of any type, including user-defined types. For example, here’s an enumeration of school grades that uses non-consecutive numeric values in descending order:
>>>fromenumimportEnum>>>classGrade(Enum):...A=90...B=80...C=70...D=60...F=0...>>>list(Grade)[ <Grade.A: 90>, <Grade.B: 80>, <Grade.C: 70>, <Grade.D: 60>, <Grade.F: 0>]This example shows that Python enums are pretty flexible and allow you to use any meaningful value for their members. You can set the member values according to the intent of your code.
You can also use string values for your enumeration members. Here’s an example of aSize enumeration that you can use in an online store:
>>>fromenumimportEnum>>>classSize(Enum):...S="small"...M="medium"...L="large"...XL="extra large"...>>>list(Size)[ <Size.S: 'small'>, <Size.M: 'medium'>, <Size.L: 'large'>, <Size.XL: 'extra large'>]In this example, the value associated with each size holds a description that can help you and other developers understand the meaning of your code.
You can also create enumerations ofBoolean values. In this case, the members of your enumeration will have only two values:
>>>fromenumimportEnum>>>classSwitchPosition(Enum):...ON=True...OFF=False...>>>list(SwitchPosition)[<SwitchPosition.ON: True>, <SwitchPosition.OFF: False>]>>>classUserResponse(Enum):...YES=True...NO=False...>>>list(UserResponse)[<UserResponse.YES: True>, <UserResponse.NO: False>]These two examples show how you can use enumerations to add extra context to your code. In the first example, anyone reading your code will know that the code emulates a switch object with two possible states. This additional information highly improves your code’s readability.
You can also define an enumeration with heterogeneous values:
>>>fromenumimportEnum>>>classUserResponse(Enum):...YES=1...NO="No"...>>>UserResponse.NO<UserResponse.NO: 'No'>>>>UserResponse.YES<UserResponse.YES: 1>However, this practice makes your code inconsistent from atype safety perspective. Therefore, it’s not recommended practice. Ideally, it would help if you had values of the same data type, which is consistent with the idea of grouping similar, related constants in enumerations.
Finally, you can also create empty enumerations:
>>>fromenumimportEnum>>>classEmpty(Enum):...pass...>>>list(Empty)[]>>>classEmpty(Enum):.........>>>list(Empty)[]>>>classEmpty(Enum):..."""Empty enumeration for such and such purposes."""...>>>list(Empty)[]In this example,Empty represents an empty enumeration because it doesn’t define any member constants. Note that you can use thepass statement, theEllipsis literal (...), or a class-leveldocstring to create empty enumerations. This last approach can help you improve the readability of your code by providing extra context in the docstring.
Now, why would you need to define an empty enumeration anyway? Empty enumerations can come in handy when you need to build a hierarchy of enum classes to reuse functionality throughinheritance.
Consider the following example:
>>>fromenumimportEnum>>>importstring>>>classBaseTextEnum(Enum):...defas_list(self):...try:...returnlist(self.value)...exceptTypeError:...return[str(self.value)]...>>>classAlphabet(BaseTextEnum):...LOWERCASE=string.ascii_lowercase...UPPERCASE=string.ascii_uppercase...>>>Alphabet.LOWERCASE.as_list()['a', 'b', 'c', 'd', ..., 'x', 'y', 'z']In this example, you createBaseTextEnum as an enumeration with no members. You can only subclass a custom enumeration if it doesn’t have members, soBaseTextEnum qualifies. TheAlphabet class inherits from your empty enumeration, which means that you can access the.as_list() method. This method converts the value of a given member into a list.
Creating Enumerations With the Functional API
TheEnum class provides afunctional API that you can use to create enumerations without using the usual class syntax. You’ll just need to callEnum with appropriate arguments like you’d do with afunction or any other callable.
This functionalAPI resembles the way in which thenamedtuple() factory function works. In the case ofEnum, the functional signature has the following form:
Enum(value,names,*,module=None,qualname=None,type=None,start=1)From this signature, you can conclude thatEnum needs twopositional arguments,value andnames. It can also take up to fouroptional andkeyword-only arguments. These arguments aremodule,qualname,type, andstart.
Here’s a table that summarizes the content and meaning of each argument in the signature ofEnum:
| Argument | Description | Required |
|---|---|---|
value | Holds a string with the name of the new enumeration class | Yes |
names | Provides names for the enumeration members | Yes |
module | Takes the name of the module that defines the enumeration class | No |
qualname | Holds the location of the module that defines the enumeration class | No |
type | Holds a class to be used as the first mixin class | No |
start | Takes the starting value from the enumeration values will begin | No |
To provide thenames argument, you can use the following objects:
- A string containing member names separated either with spaces or commas
- An iterable of member names
- An iterable of name-value pairs
Themodule andqualname arguments play an important role when you need topickle and unpickle your enumerations. Ifmodule isn’t set, then Python will attempt to find the module. If it fails, then the class will not be picklable. Similarly, ifqualname isn’t set, then Python will set it to theglobal scope, which may cause your enumerations to fail unpickling in some situations.
Thetype argument is required when you want to provide amixin class for your enumeration. Using a mixin class can provide your custom enum with new functionality, such as extended comparison capabilities, as you’ll learn in the section aboutmixing enumerations with other data types.
Finally, thestart argument provides a way to customize the initial value of your enumerations. This argument defaults to1 rather than to0. The reason for this default value is that0 is false in a Boolean sense, but enum members evaluate toTrue. Therefore, starting from0 would seem surprising and confusing.
Most of the time, you’ll just use the first two arguments toEnum when creating your enumerations. Here’s an example of creating an enumeration of commonHTTP methods:
>>>fromenumimportEnum>>>HTTPMethod=Enum(..."HTTPMethod",["GET","POST","PUSH","PATCH","DELETE"]...)>>>list(HTTPMethod)[ <HTTPMethod.GET: 1>, <HTTPMethod.POST: 2>, <HTTPMethod.PUSH: 3>, <HTTPMethod.PATCH: 4>, <HTTPMethod.DELETE: 5>]This call toEnumreturns a new enumeration calledHTTPMethod. To provide the member names, you use a list of strings. Each string represents an HTTP method. Note that the member values are automatically set to consecutive integer numbers starting from1. You can change this initial value using thestart argument.
Note that defining the above enumerations with the class syntax will produce the same result:
>>>fromenumimportEnum>>>classHTTPMethod(Enum):...GET=1...POST=2...PUSH=3...PATCH=4...DELETE=5...>>>list(HTTPMethod)[ <HTTPMethod.GET: 1>, <HTTPMethod.POST: 2>, <HTTPMethod.PUSH: 3>, <HTTPMethod.PATCH: 4>, <HTTPMethod.DELETE: 5>]Here, you use the class syntax to define theHTTPMethod enum. This example is completely equivalent to the previous one, as you can conclude from the output oflist().
Using either the class syntax or the functional API to create your enumeration is your decision and will mostly depend on your taste and concrete conditions. However, if you want to create enumerations dynamically, then the functional API can be your only option.
Consider the following example, where you create an enum with user-provided members:
>>>fromenumimportEnum>>>names=[]>>>whileTrue:...name=input("Member name: ")...ifnamein{"q","Q"}:...break...names.append(name.upper())...Member name: YESMember name: NOMember name: q>>>DynamicEnum=Enum("DynamicEnum",names)>>>list(DynamicEnum)[<DynamicEnum.YES: 1>, <DynamicEnum.NO: 2>]This example is a little bit extreme because creating any object from your user’s input is quite a risky practice, considering that you can’t predict what the user will input. However, the example is intended to show that the functional API is the way to go when you need to create enumerations dynamically.
Finally, if you need to set custom values for your enum members, then you can use an iterable of name-value pairs as yournames argument. In the example below, you use a list of name-value tuples to initialize all the enumeration members:
>>>fromenumimportEnum>>>HTTPStatusCode=Enum(...value="HTTPStatusCode",...names=[...("OK",200),...("CREATED",201),...("BAD_REQUEST",400),...("NOT_FOUND",404),...("SERVER_ERROR",500),...],...)>>>list(HTTPStatusCode)[ <HTTPStatusCode.OK: 200>, <HTTPStatusCode.CREATED: 201>, <HTTPStatusCode.BAD_REQUEST: 400>, <HTTPStatusCode.NOT_FOUND: 404>, <HTTPStatusCode.SERVER_ERROR: 500>]Providing a list of name-value tuples like you did above makes it possible to create theHTTPStatusCode enumeration with custom values for the members. In this example, if you didn’t want to use a list of name-value tuples, then you could also use a dictionary that maps names to values.
Building Enumerations From Automatic Values
Python’senum module provides a convenient function calledauto() that allows you to set automatic values for your enum members. This function’s default behavior is to assign consecutive integer values to members.
Here’s howauto() works:
>>>fromenumimportauto,Enum>>>classDay(Enum):...MONDAY=auto()...TUESDAY=auto()...WEDNESDAY=3...THURSDAY=auto()...FRIDAY=auto()...SATURDAY=auto()...SUNDAY=7...>>>list(Day)[ <Day.MONDAY: 1>, <Day.TUESDAY: 2>, <Day.WEDNESDAY: 3>, <Day.THURSDAY: 4>, <Day.FRIDAY: 5>, <Day.SATURDAY: 6>, <Day.SUNDAY: 7>]You need to callauto() once for each automatic value that you need. You can also combineauto() with concrete values, just like you did withDay.WEDNESDAY andDay.SUNDAY in this example.
By default,auto() assigns consecutive integer numbers to each target member starting from1. You can tweak this default behavior by overriding the._generate_next_value_() method, whichauto() uses under the hood to generate the automatic values.
Here’s an example of how to do this:
>>>fromenumimportEnum,auto>>>classCardinalDirection(Enum):...def_generate_next_value_(name,start,count,last_values):...returnname[0]...NORTH=auto()...SOUTH=auto()...EAST=auto()...WEST=auto()...>>>list(CardinalDirection)[ <CardinalDirection.NORTH: 'N'>, <CardinalDirection.SOUTH: 'S'>, <CardinalDirection.EAST: 'E'>, <CardinalDirection.WEST: 'W'>]In this example, you create an enumeration of Earth’scardinal directions in which values are automatically set to strings containing the first character of each member’s name. Note that you must provide your overridden version of._generate_next_value_() before defining any members. That’s because the members will be built by calling the method.
Creating Enumerations With Aliases and Unique Values
You can create enumerations in which two or more members have the same constant value. The redundant members are known asaliases and can be useful in some situations. For example, say that you have an enum containing a set of operating systems (OS), like in the following code:
>>>fromenumimportEnum>>>classOperatingSystem(Enum):...UBUNTU="linux"...MACOS="darwin"...WINDOWS="win"...DEBIAN="linux"...>>># Aliases aren't listed>>>list(OperatingSystem)[ <OperatingSystem.UBUNTU: 'linux'>, <OperatingSystem.MACOS: 'darwin'>, <OperatingSystem.WINDOWS: 'win'>]>>># To access aliases, use __members__>>>list(OperatingSystem.__members__.items())[ ('UBUNTU', <OperatingSystem.UBUNTU: 'linux'>), ('MACOS', <OperatingSystem.MACOS: 'darwin'>), ('WINDOWS', <OperatingSystem.WINDOWS: 'win'>), ('DEBIAN', <OperatingSystem.UBUNTU: 'linux'>)]Linux distributions are considered independent operating systems. So, Ubuntu and Debian are both independent systems with different goals and target audiences. However, they share a commonkernel called Linux.
The above enumeration maps operating systems to their corresponding kernels. This relationship turnsDEBIAN into an alias ofUBUNTU, which may be useful when you have code that’s kernel-related along with code that’s specific to a given Linux distribution.
An important piece of behavior to note in the above example is that when you iterate over the enumeration directly, aliases aren’t considered. If you ever need to iterate over all the members, including aliases, then you need to use.__members__. You’ll learn more about iteration and the.__members__ attribute in the section aboutiterating through enumerations.
You also have the option to completely forbid aliases in your enumerations. To do this, you can use the@uniquedecorator from theenum module:
>>>fromenumimportEnum,unique>>>@unique...classOperatingSystem(Enum):...UBUNTU="linux"...MACOS="darwin"...WINDOWS="win"...DEBIAN="linux"...Traceback (most recent call last):...ValueError:duplicate values in <enum 'OperatingSystem'>: DEBIAN -> UBUNTUIn this example, you decorateOperatingSystem with@unique. If any member value is duplicated, then you get aValueError. Here, the exception message points out thatDEBIAN andUBUNTU share the same value, which isn’t allowed.
Working With Enumerations in Python
Up to this point, you’ve learned what enumerations are, when to use them, and what benefits you get from using them in your code. You’ve also leaned how to create enumerations in Python using theEnum class either as a superclass or as a callable.
Now it’s time for you to start digging into how Python’s enumerations work and how you can use them in your code.
Accessing Enumeration Members
When it comes to using enumerations in your code, accessing their members is a fundamental operation to perform. You’ll have three different ways to access enumeration members in Python.
For example, say that you need to access theNORTH member of theCardinalDirection enum below. In this situation, you can do something like this:
>>>fromenumimportEnum>>>classCardinalDirection(Enum):...NORTH="N"...SOUTH="S"...EAST="E"...WEST="W"...>>># Dot notation>>>CardinalDirection.NORTH<CardinalDirection.NORTH: 'N'>>>># Call notation>>>CardinalDirection("N")<CardinalDirection.NORTH: 'N'>>>># Subscript notation>>>CardinalDirection["NORTH"]<CardinalDirection.NORTH: 'N'>The first highlighted line in this example shows how you can access an enum member using thedot notation, which is pretty intuitive and readable. The second highlighted line accesses the target member bycalling the enumeration with the member’s value as an argument.
Note: It’s important to note that calling an enumeration with a member’s value as an argument can make you feel like you’re instantiating the enumeration. However, enumerations can’t be instantiated, as you already know:
>>>week=Day()Traceback (most recent call last):...TypeError:EnumMeta.__call__() missing 1 required positional argument: 'value'Trying to create an instance of an existing enumeration isn’t allowed, so you get aTypeError if you attempt to do it. Therefore, you must not confuse instantiating with accessing members through an enumeration call.
Finally, the third highlighted line shows how you can use adictionary-like notation orsubscript notation to access a member using the member’s name as the target key.
Python’s enumerations offer great flexibility for you to access members. The dot notation is arguably the most commonly used approach in Python code. However, the other two approaches can be helpful as well. So, use the notation that fulfills your specific needs, conventions, and style.
Using the.name and.value Attributes
The members of a Python enumeration are instances of their containing class. During the enum class parsing, each member is automatically provided with a.name attribute that holds the member’s name as a string. Members also get a.value attribute that stores the value assigned to the member itself in the class definition.
You can access.name and.value as you’d do with a regular attribute, using the dot notation. Consider the following example, which simulates a semaphore, more commonly known as a traffic light:
>>>fromenumimportEnum>>>classSemaphore(Enum):...RED=1...YELLOW=2...GREEN=3...>>>Semaphore.RED.name'RED'>>>Semaphore.RED.value1>>>Semaphore.YELLOW.name'YELLOW'The.name and.value attributes of an enum member give you direct access to the member’s name as a string and to the member’s value, respectively. These attributes come in handy when you’re iterating through your enumerations, which you’ll explore in the next section.
Iterating Through Enumerations
A remarkable feature of Python enumerations compared to regular classes is that enumerations are iterable by default. Because they’re iterable, you can use them infor loops and with other tools that accept and process iterables.
Python’s enumerations support direct iteration over members in the definition order:
>>>fromenumimportEnum>>>classFlavor(Enum):...VANILLA=1...CHOCOLATE=2...MINT=3...>>>forflavorinFlavor:...print(flavor)...Flavor.VANILLAFlavor.CHOCOLATEFlavor.MINTIn this example, you use afor loop to iterate over the members ofFlavor. Note that members are produced in the same order as they were defined in the class definition.
When you’re iterating over an enumeration, you can access the.name and.value attributes as you go:
>>>forflavorinFlavor:...print(flavor.name,"->",flavor.value)...VANILLA -> 1CHOCOLATE -> 2MINT -> 3This kind of iteration technique looks pretty similar toiterating over a dictionary. So, if you’re familiar with dictionary iteration, then looping over enumerations using this technique will be a straightforward task with many potential use cases.
Alternatively, enumerations have a special attribute called.__members__ that you can also use for iterating over their members. This attribute holds a dictionary that maps names to members. The difference between iterating over this dictionary and over the enumeration directly is that the dictionary gives you access to all members of the enumeration, including all the aliases that you may have.
Here are some examples of using.__members__ to iterate through yourFlavor enumeration:
>>>fornameinFlavor.__members__:...print(name)...VANILLACHOCOLATEMINT>>>fornameinFlavor.__members__.keys():...print(name)...VANILLACHOCOLATEMINT>>>formemberinFlavor.__members__.values():...print(member)...Flavor.VANILLAFlavor.CHOCOLATEFlavor.MINT>>>forname,memberinFlavor.__members__.items():...print(name,"->",member)...VANILLA -> Flavor.VANILLACHOCOLATE -> Flavor.CHOCOLATEMINT -> Flavor.MINTYou can use the.__members__ special attribute for detailed programmatic access to the members of a Python enumeration. Because.__members__ holds a regular dictionary, you can use all the iteration techniques that apply to this built-in data type. Some of these techniques include using dictionary methods like.key(),.values(), and.items().
Using Enumerations inif andmatch Statements
Chainedif …elif statements and the relatively newmatch …case statement are common and arguably natural places where you can use enumerations. Both constructs allow you to take different courses of action depending on certain conditions.
For example, say that you have a piece of code that handles a semaphore, or traffic light, in a traffic control application. You must perform different actions depending on the current light of the semaphore. In this situation, you can use an enumeration to represent the semaphore and its lights. Then you can use a chain ofif …elif statements to decide on the action to run:
>>>fromenumimportEnum>>>classSemaphore(Enum):...RED=1...YELLOW=2...GREEN=3...>>>defhandle_semaphore(light):...iflightisSemaphore.RED:...print("You must stop!")...eliflightisSemaphore.YELLOW:...print("Light will change to red, be careful!")...eliflightisSemaphore.GREEN:...print("You can continue!")...>>>handle_semaphore(Semaphore.GREEN)You can continue!>>>handle_semaphore(Semaphore.YELLOW)Light will change to red, be careful!>>>handle_semaphore(Semaphore.RED)You must stop!The chain ofif …elif statements in yourhandle_semaphore() function checks the value of the current light to decide on the action to take. Note that the calls toprint() inhandle_semaphore() are just placeholders. In real code, you’d replace them with more complex operations.
If you’re usingPython 3.10 or greater, then you can quickly turn the above chain ofif …elif statements into an equivalentmatch …case statement:
>>>fromenumimportEnum>>>classSemaphore(Enum):...RED=1...YELLOW=2...GREEN=3...>>>defhandle_semaphore(light):...matchlight:...caseSemaphore.RED:...print("You must stop!")...caseSemaphore.YELLOW:...print("Light will change to red, be careful!")...caseSemaphore.GREEN:...print("You can continue!")...>>>handle_semaphore(Semaphore.GREEN)You can continue!>>>handle_semaphore(Semaphore.YELLOW)Light will change to red, be careful!>>>handle_semaphore(Semaphore.RED)You must stop!This new implementation ofhandle_semaphore() is equivalent to the previous implementation that usesif …elif statements. Using either technique is a matter of taste and style. Both techniques work well and are comparable in terms of readability. However, note that if you need to guarantee backward compatibility with Python versions lower than 3.10, then you must use chainedif …elif statements.
Finally, note that even though enumerations seem to play well withif …elif andmatch …case statements, you must keep in mind that these statements don’t scale well. If you add new members to your target enumeration, then you’ll need to update the handling function to consider these new members.
Comparing Enumerations
Being able to use enumerations inif …elif statements andmatch …case statements suggests that enumeration members can be compared. By default, enums support two types of comparison operators:
The identity comparison relies on the fact that each enum member is asingleton instance of its enumeration class. This characteristic allows for fast and cheap identity comparison of members using theis andis not operators.
Consider the following examples, which compare different combinations of enum members:
>>>fromenumimportEnum>>>classAtlanticAveSemaphore(Enum):...RED=1...YELLOW=2...GREEN=3...PEDESTRIAN_RED=1...PEDESTRIAN_GREEN=3...>>>red=AtlanticAveSemaphore.RED>>>redisAtlanticAveSemaphore.REDTrue>>>redisnotAtlanticAveSemaphore.REDFalse>>>yellow=AtlanticAveSemaphore.YELLOW>>>yellowisredFalse>>>yellowisnotredTrue>>>pedestrian_red=AtlanticAveSemaphore.PEDESTRIAN_RED>>>redispedestrian_redTrueEvery enum member has its own identity, which is different from the identity of its sibling members. This rule doesn’t apply to member aliases, because they’re just references to existing members and share the same identity. This is why comparingred andpedestrian_red returnsTrue in your final example.
Note: To get the identity of a given object in Python, you can use the built-inid() function with the object as an argument.
Identity checks between members of different enumerations always returnFalse:
>>>classEighthAveSemaphore(Enum):...RED=1...YELLOW=2...GREEN=3...PEDESTRIAN_RED=1...PEDESTRIAN_GREEN=3...>>>AtlanticAveSemaphore.REDisEighthAveSemaphore.REDFalse>>>AtlanticAveSemaphore.YELLOWisEighthAveSemaphore.YELLOWFalseThe reason for this falsy result is that members of different enums are independent instances with their own identities, so any identity check on them returnsFalse.
The equality operators== and!= also work between enumeration members:
>>>fromenumimportEnum>>>classAtlanticAveSemaphore(Enum):...RED=1...YELLOW=2...GREEN=3...PEDESTRIAN_RED=1...PEDESTRIAN_GREEN=3...>>>red=AtlanticAveSemaphore.RED>>>red==AtlanticAveSemaphore.REDTrue>>>red!=AtlanticAveSemaphore.REDFalse>>>yellow=AtlanticAveSemaphore.YELLOW>>>yellow==redFalse>>>yellow!=redTrue>>>pedestrian_red=AtlanticAveSemaphore.PEDESTRIAN_RED>>>red==pedestrian_redTruePython’s enumerations support both operators,== and!=, by delegating to theis andis not operators, respectively.
As you already learned, enum members always have a concrete value that can be a number, a string, or any other object. Because of this, running equality comparisons between enum members and common objects can be tempting.
However, this kind of comparison doesn’t work as expected because the actual comparison is based on object identity:
>>>fromenumimportEnum>>>classSemaphore(Enum):...RED=1...YELLOW=2...GREEN=3...>>>Semaphore.RED==1False>>>Semaphore.YELLOW==2False>>>Semaphore.GREEN!=3TrueEven though the member values are equal to the integers in each example, these comparisons returnFalse. This is because regular enum members compare by object identity rather than by value. In the example above, you’re comparing enum members to integer numbers, which is like comparing apples and oranges. They’ll never compare equally, because they have different identities.
Note:Later, you’ll learn aboutIntEnum which are special enumerations that can be compared to integers.
Finally, another comparison-related feature of enumerations is that you can perform membership tests on them using thein andnot in operators:
>>>fromenumimportEnum>>>classSemaphore(Enum):...RED=1...YELLOW=2...GREEN=3...>>>Semaphore.REDinSemaphoreTrue>>>Semaphore.GREENnotinSemaphoreFalsePython’s enumerations support thein andnot in operators by default. Using these operators, you can check if a given member is present in a given enumeration.
Sorting Enumerations
By default, Python’s enums don’t support comparison operators like>,<,>=, and<=. That’s why you can’t sort the members of an enumeration using the built-insorted() function directly, like in the example below:
>>>fromenumimportEnum>>>classSeason(Enum):...SPRING=1...SUMMER=2...AUTUMN=3...WINTER=4...>>>sorted(Season)Traceback (most recent call last):...TypeError:'<' not supported between instances of 'Season' and 'Season'When you use an enumeration as an argument tosorted(), you get aTypeError because enums don’t support the< operator. However, there’s a way to successfully sort enumerations by their members’ names and values using thekey argument in thesorted() call.
Here’s how to do it:
>>>sorted(Season,key=lambdaseason:season.value)[ <Season.SPRING: 1>, <Season.SUMMER: 2>, <Season.AUTUMN: 3>, <Season.WINTER: 4>]>>>sorted(Season,key=lambdaseason:season.name)[ <Season.AUTUMN: 3>, <Season.SPRING: 1>, <Season.SUMMER: 2>, <Season.WINTER: 4>]In the first example, you use alambda function that takes an enumeration member as an argument and returns its.value attribute. With this technique, you can sort the input enumeration by its values. In the second example, thelambda function takes an enum member and returns its.name attribute. This way, you can sort the enumeration by the names of its members.
Extending Enumerations With New Behavior
In the previous sections, you’ve learned how to create and use enumerations in your Python code. Up to this point, you’ve worked with default enumerations. This means that you’ve used Python’s enumerations with their standard features and behaviors only.
Sometimes, you may need to provide your enumerations with custom behavior. To do this, you can add methods to your enums and implement the required functionality. You can also use mixin classes. In the following sections, you’ll learn how to take advantage of both techniques to customize your enumerations.
Adding and Tweaking Member Methods
You can provide your enumerations with new functionality by adding new methods to your enumeration classes as you’d do with any regular Python class. Enumerations are classes with special features. Like regular classes, enumerations can have methods andspecial methods.
Consider the following example, adapted from thePython documentation:
>>>fromenumimportEnum>>>classMood(Enum):...FUNKY=1...MAD=2...HAPPY=3......defdescribe_mood(self):...returnself.name,self.value......def__str__(self):...returnf"I feel{self.name}"......@classmethod...deffavorite_mood(cls):...returncls.HAPPY...>>>Mood.HAPPY.describe_mood()('HAPPY', 3)>>>print(Mood.HAPPY)I feel HAPPY>>>Mood.favorite_mood()<Mood.HAPPY: 3>In this example, you have aMood enumeration with three members. Regular methods like.describe_mood() are bound to instances of their containing enum, which are the enum members. So, you must call regular methods on enum members rather than on the enum class itself.
Note: Remember that Python’s enumerations can’t be instantiated. The members of an enumeration are the enumeration’s allowed instances. So, theself parameter represents the current member.
Similarly, the.__str__() special method operates on members, providing a nicely printable representation of each member.
Finally, the.favorite_mood() method is aclass method, which operates on the class or enumeration itself. Class methods like this one provide access to all the enum members from inside the class.
You can also take advantage of this ability to contain additional behavior when you need to implement thestrategy pattern. For example, say you need a class that allows you to use two strategies for sorting a list of numbers in ascending and descending order. In this case, you can use an enumeration like the following:
>>>fromenumimportEnum>>>classSort(Enum):...ASCENDING=1...DESCENDING=2...def__call__(self,values):...returnsorted(values,reverse=selfisSort.DESCENDING)...>>>numbers=[5,2,7,6,3,9,8,4]>>>Sort.ASCENDING(numbers)[2, 3, 4, 5, 6, 7, 8, 9]>>>Sort.DESCENDING(numbers)[9, 8, 7, 6, 5, 4, 3, 2]Each member ofSort represents a sorting strategy. The.__call__() method makes the members ofSortcallable. Inside.__call__(), you use the built-insorted() function to sort the input values in ascending or descending order, depending on the called member.
Note: The above example is intended to be a demonstrative example of using an enum to implement the strategydesign pattern. In practice, it’s unnecessary to create thisSort enum with the sole purpose of wrapping thesorted() function. Instead, you may usesorted() and itsreverse argument directly and avoid overengineering your solution.
When you callSort.ASCENDING, the input numbers are sorted in ascending order. In contrast, when you callSort.DESCENDING, the numbers get sorted in descending order. That’s it! You’ve used an enumeration to quickly implement the strategy design pattern.
Mixing Enumerations With Other Types
Python supportsmultiple inheritance as part of its object-oriented features. This means that in Python, you can inherit multiple classes when creating class hierarchies. Multiple inheritance comes in handy when you want to reuse functionality from several classes at the same time.
A common practice in object-oriented programming is to use what’s known asmixin classes. These classes provide functionality that other classes can use. In Python, you can add mixin classes to the list of parents of a given class to automatically get the mixin functionality.
For example, say that you want an enumeration that supports integer comparison. In this case, you can use the built-inint type as a mixin when defining your enum:
>>>fromenumimportEnum>>>classSize(int,Enum):...S=1...M=2...L=3...XL=4...>>>Size.S>Size.MFalse>>>Size.S<Size.MTrue>>>Size.L>=Size.MTrue>>>Size.L<=Size.MFalse>>>Size.L>2True>>>Size.M<1FalseIn this example, yourSize class inherits fromint andEnum. Inheriting from theint type enables direct comparison between members through the>,<,>=, and<= comparison operators. It also enables comparisons betweenSize members and integer numbers.
Finally, note that when you use a data type as a mixin, the member’s.value attribute isn’t the same as the member itself, although it’s equivalent and will compare as such. That’s why you can compare the members ofSize with integer numbers directly.
Note: Using integer enum member values is a pretty common practice. That’s why theenum module provides anIntEnum to create enumerations with integer values directly. You’ll learn more about this class in the section calledExploring Other Enumeration Classes.
The above example shows that creating enumerations with mixin classes is often of great help when you need to reuse a given piece of functionality. If you decide to use this technique in some of your enums, then you’ll have to stick to the following signature:
classEnumName([mixin_type,...],[data_type,]enum_type):# Members go here...This signature implies that you can have one or more mixin classes, at most one data type class, and the parent enum class, in that order.
Consider the following examples:
>>>fromenumimportEnum>>>classMixinA:...defa(self):...print(f"MixinA:{self.value}")...>>>classMixinB:...defb(self):...print(f"MixinB:{self.value}")...>>>classValidEnum(MixinA,MixinB,str,Enum):...MEMBER="value"...>>>ValidEnum.MEMBER.a()# Call .a() from MixinAMixinA: value>>>ValidEnum.MEMBER.b()# Call .b() from MixinBMixinB: value>>>ValidEnum.MEMBER.upper()# Call .upper() from str'VALUE'>>>classWrongMixinOrderEnum(Enum,MixinA,MixinB):...MEMBER="value"...Traceback (most recent call last):...TypeError:new enumerations should be created as `EnumName([mixin_type, ...] [data_type,] enum_type)`>>>classTooManyDataTypesEnum(int,str,Enum):...MEMBER="value"...Traceback (most recent call last):...TypeError:'TooManyDataTypesEnum': too many data types: {<class 'int'>, <class 'str'>}TheValidEnum class shows that in the sequence of bases, you must place as many mixin classes as you need—but only one data type—beforeEnum.
WrongMixinOrderEnum shows that if you putEnum in any position other than the last, then you’ll get aTypeError with information about the correct signature to use. Meanwhile,TooManyDataTypesEnum confirms that your list of mixin classes must have at most one concrete data type, such asint orstr.
Keep in mind that if you use a concrete data type in your list of mixin classes, then the member values have to match the type of this specific data type.
Exploring Other Enumeration Classes
Apart fromEnum, theenum module provides a few additional classes that allow you to create enumerations with specific behaviors. You’ll have theIntEnum class for creating enumerated constants that are also subclasses ofint, which implies that all members will have all the features of an integer number.
You’ll also find more specialized classes, likeIntFlag andFlag. Both classes will allow you to create enumerated sets of constants that you can combine using thebitwise operators. In the following section, you’ll explore these classes and how they work in Python.
Building Integer Enumerations:IntEnum
Integer enumerations are so common that theenum module exports a dedicated class calledIntEnum that was specifically created to cover this use case. If you need the members of your enumerations to behave like integer numbers, then you should inherit fromIntEnum rather than fromEnum.
SubclassingIntEnum is equivalent to using multiple inheritance withint as the mixin class:
>>>fromenumimportIntEnum>>>classSize(IntEnum):...S=1...M=2...L=3...XL=4...>>>Size.S>Size.MFalse>>>Size.S<Size.MTrue>>>Size.L>=Size.MTrue>>>Size.L<=Size.MFalse>>>Size.L>2True>>>Size.M<1FalseNowSize inherits directly fromIntEnum instead of fromint andEnum. Like the previous version ofSize, this new version has full comparison capabilities and supports all the comparison operators. You can also use the class members in integer operations directly.
Size will automatically attempt to convert any value of a different data type to an integer number. If this conversion isn’t possible, then you’ll get aValueError:
>>>fromenumimportIntEnum>>>classSize(IntEnum):...S=1...M=2...L=3...XL="4"...>>>list(Size)[<Size.S: 1>, <Size.M: 2>, <Size.L: 3>, <Size.XL: 4>]>>>classSize(IntEnum):...S=1...M=2...L=3...XL="4.o"...Traceback (most recent call last):...ValueError:invalid literal for int() with base 10: '4.o'In the first example,Size automatically converts the string"4" into an integer value. In the second example, because the string"4.o" doesn’t hold a valid numeric value, you get aValueError, and the conversion fails.
In the current stable Python version,3.10, theenum module doesn’t include aStrEnum class. However, this class is another example of a popular use case of enumerations. For this reason, Python 3.11 will include aStrEnum type with direct support for common string operations. In the meantime, you can simulate the behavior of aStrEnum class by creating a mixin class withstr andEnum as parent classes.
Creating Integer Flags:IntFlag andFlag
You can useIntFlag as the base class for enumerations that should support the bitwise operators. Performing bitwise operations on members of anIntFlag subclass will return an object that’s also a member of the underlying enum.
Here’s an example of aRole enumeration that lets you manage different user roles in a single combined object:
>>>fromenumimportIntFlag>>>classRole(IntFlag):...OWNER=8...POWER_USER=4...USER=2...SUPERVISOR=1...ADMIN=OWNER|POWER_USER|USER|SUPERVISOR...>>>john_roles=Role.USER|Role.SUPERVISOR>>>john_roles<Role.USER|SUPERVISOR: 3>>>>type(john_roles)<enum 'Role'>>>>ifRole.USERinjohn_roles:...print("John, you're a user")...John, you're a user>>>ifRole.SUPERVISORinjohn_roles:...print("John, you're a supervisor")...John, you're a supervisor>>>Role.OWNERinRole.ADMINTrue>>>Role.SUPERVISORinRole.ADMINTrueIn this code snippet, you create an enumeration that holds a set of user roles in a given application. The members of this enumeration hold integer values that you can combine using the bitwise OR operator (|). For example, the user named John has bothUSER andSUPERVISOR roles. Note that the object stored injohn_roles is a member of yourRole enumeration.
Note: You should keep in mind that individual members of enums based onIntFlag, also known asflags, should take values that are powers of two (1, 2, 4, 8, …). However, this isn’t a requirement for combinations of flags, likeRole.ADMIN.
In the above example, you definedRole.ADMIN as a combination of roles. Its value results from applying the bitwise OR operator to the complete list of previous roles in the enumeration.
IntFlag also supports integer operations, such as arithmetic and comparison operations. However, these types of operations return integers rather than member objects:
>>>Role.ADMIN+116>>>Role.ADMIN-213>>>Role.ADMIN/35.0>>>Role.ADMIN<20TrueIntFlag members are also subclasses ofint. That’s why you can use them in expressions that involve integer numbers. In these situations, the resulting value will be an integer rather than an enum member.
Finally, you’ll also find theFlag class available inenum. This class works similarly toIntFlag and has some additional restrictions:
>>>fromenumimportFlag>>>classRole(Flag):...OWNER=8...POWER_USER=4...USER=2...SUPERVISOR=1...ADMIN=OWNER|POWER_USER|USER|SUPERVISOR...>>>john_roles=Role.USER|Role.SUPERVISOR>>>john_roles<Role.USER|SUPERVISOR: 3>>>>type(john_roles)<enum 'Role'>>>>ifRole.USERinjohn_roles:...print("John, you're a user")...John, you're a user>>>ifRole.SUPERVISORinjohn_roles:...print("John, you're a supervisor")...John, you're a supervisor>>>Role.OWNERinRole.ADMINTrue>>>Role.SUPERVISORinRole.ADMINTrue>>>Role.ADMIN+1Traceback (most recent call last):...TypeError:unsupported operand type(s) for +: 'Role' and 'int'The main difference betweenIntFlag andFlag is that the latter doesn’t inherit fromint. Therefore, integer operations aren’t supported. When you try to use a member ofRole in an integer operation, you get aTypeError.
Just like members ofIntFlag enums, the members ofFlag enums should have values that are powers of two. Again, this doesn’t apply to combinations of flags, likeRole.ADMIN in the example above.
Using Enumerations: Two Practical Examples
Python’s enumerations can help you improve your code’s readability and organization. You can use them to group similar constants that you can then use in your code to replace strings, numbers, and other values with readable and meaningful names.
In the following sections, you’ll code a couple of practical examples that deal with common enum use cases. These examples will help you decide when your code could benefit from using enumerations.
Replacing Magic Numbers
Enumerations are great when you need to replace sets of related magic numbers, such as HTTP status codes, computer ports, and exit codes. With an enumeration, you can group these numeric constants and assign them readable and descriptive names that you can use and reuse in your code later.
Say that you have the following function as part of an application that retrieves and processes HTTP content directly from the web:
>>>fromhttp.clientimportHTTPSConnection>>>defprocess_response(response):...matchresponse.getcode():...case200:...print("Success!")...case201:...print("Successfully created!")...case400:...print("Bad request")...case404:...print("Not Found")...case500:...print("Internal server error")...case_:...print("Unexpected status")...>>>connection=HTTPSConnection("www.python.org")>>>try:...connection.request("GET","/")...response=connection.getresponse()...process_response(response)...finally:...connection.close()...Success!Yourprocess_response() function takes an HTTPresponse object as an argument. Then it gets the status code fromresponse using the.getcode() method. Thematch …case statement sequentially compares the current status code with some standard status codes provided as magic numbers in your example.
If a match occurs, then the code block in the matchedcase runs. If no match occurs, then the defaultcase runs. Note that the defaultcase is the one that uses an underscore (_) as a match criterion.
The rest of the code connects to a sample web page, performs aGET request, retrieves the response object, and processes it using yourprocess_response() function. Thefinally clause closes the active connection to avoid resource leaks.
Even though this code works, it can be challenging to read and understand for people unfamiliar with HTTP status codes and their corresponding meanings. To fix these issues and make your code more readable and maintainable, you can use an enumeration to group the HTTP status codes and give them descriptive names:
>>>fromenumimportIntEnum>>>fromhttp.clientimportHTTPSConnection>>>classHTTPStatusCode(IntEnum):...OK=200...CREATED=201...BAD_REQUEST=400...NOT_FOUND=404...SERVER_ERROR=500...>>>defprocess_response(response):...matchresponse.getcode():...caseHTTPStatusCode.OK:...print("Success!")...caseHTTPStatusCode.CREATED:...print("Successfully created!")...caseHTTPStatusCode.BAD_REQUEST:...print("Bad request")...caseHTTPStatusCode.NOT_FOUND:...print("Not Found")...caseHTTPStatusCode.SERVER_ERROR:...print("Internal server error")...case_:...print("Unexpected status")...>>>connection=HTTPSConnection("www.python.org")>>>try:...connection.request("GET","/")...response=connection.getresponse()...process_response(response)...finally:...connection.close()...Success!This code adds a new enumeration calledHTTPStatusCode to your application. This enum groups the target HTTP status codes and gives them readable names. It also makes them strictly constant, which makes your app more reliable.
Insideprocess_response(), you use human-readable and descriptive names that provide context and content information. Now anyone reading your code will immediately know that the match criteria are HTTP status codes. They’ll also spot the meaning of each target code quickly.
Creating a State Machine
Another interesting use case of enumerations is when you use them for re-creating the different possiblestates of a given system. If your system can be in exactly one of a finite number of states at any given time, then your system works as astate machine. Enumerations are useful when you need to implement this common design pattern.
As an example of how to use an enum to implement the state machine pattern, you create a minimaldisk player simulator. To start, go ahead and create adisk_player.py file with the following content:
disk_player.pyfromenumimportEnum,autoclassState(Enum):EMPTY=auto()STOPPED=auto()PAUSED=auto()PLAYING=auto()Here, you define theState class. This class groups all the possible states of your disk player:EMPTY,STOPPED,PAUSED, andPLAYING. Now you can code theDiskPlayer player class, which would look something like this:
disk_player.py# ...classDiskPlayer:def__init__(self):self.state=State.EMPTYdefinsert_disk(self):ifself.stateisState.EMPTY:self.state=State.STOPPEDelse:raiseValueError("disk already inserted")defeject_disk(self):ifself.stateisState.EMPTY:raiseValueError("no disk inserted")else:self.state=State.EMPTYdefplay(self):ifself.statein{State.STOPPED,State.PAUSED}:self.state=State.PLAYINGdefpause(self):ifself.stateisState.PLAYING:self.state=State.PAUSEDelse:raiseValueError("can't pause when not playing")defstop(self):ifself.statein{State.PLAYING,State.PAUSED}:self.state=State.STOPPEDelse:raiseValueError("can't stop when not playing or paused")TheDiskPlayer class implements all the possible actions that your player can execute, including inserting and ejecting the disk, playing, pausing, and stopping the player. Note how each method inDiskPlayer checks and updates the player’s current state by taking advantage of yourState enumeration.
To complete your example, you’ll use the traditionalif __name__ == "__main__": idiom to wrap up a few lines of code that’ll allow you to try out theDiskPlayer class:
disk_player.py# ...if__name__=="__main__":actions=[DiskPlayer.insert_disk,DiskPlayer.play,DiskPlayer.pause,DiskPlayer.stop,DiskPlayer.eject_disk,DiskPlayer.insert_disk,DiskPlayer.play,DiskPlayer.stop,DiskPlayer.eject_disk,]player=DiskPlayer()foractioninactions:action(player)print(player.state)In this code snippet, you first define anactionsvariable, which holds the sequence of methods that you’ll call fromDiskPlayer in order to try out the class. Then you create an instance of your disk player class. Finally, you start afor loop to iterate over the list of actions and run every action through theplayer instance.
That’s it! Your disk player simulator is ready for a test. To run it, go ahead and execute the following command at your command line:
$pythondisk_player.pyState.STOPPEDState.PLAYINGState.PAUSEDState.STOPPEDState.EMPTYState.STOPPEDState.PLAYINGState.STOPPEDState.EMPTYThis command’s output shows that your app has gone through all the possible states. Of course, this example is minimal and doesn’t consider all the potential scenarios. It’s a demonstrative example of how you could use an enumeration to implement the state machine pattern in your code.
Conclusion
You now know how to create and useenumerations in Python. Enumerations, or justenums, are common and popular data types in many programming languages. With enumerations, you can group sets of related constants and access them through the enumeration itself.
Python doesn’t provide a dedicated enum syntax. However, theenum module supports this common data type through theEnum class.
In this tutorial, you’ve learned how to:
- Create your ownenumerations using Python’s
Enumclass - Work with enumerations and theirmembers
- Provide your enumeration classes withnew functionalities
- Use enumerations with somepractical examples
You also learned about other useful enumeration types, such asIntEnum,IntFlag, andFlag. They’re available inenum and will help you create specialized enums.
With all this knowledge, you’re now ready to start using Python’s enums to group, name, and handle sets of semantically related constants. Enumerations allow you to better organize your code, making it more readable, explicit, and maintainable.
Get Your Code:Click here to download the free sample code that you’ll use to build enumerations in Python.
Frequently Asked Questions
Now that you have some experience with Python’senum module, you can use the questions and answers below to check your understanding and recap what you’ve learned.
These FAQs are related to the most important concepts you’ve covered in this tutorial. Click theShow/Hide toggle beside each question to reveal the answer.
In Python, an enum is a data type that allows you to create a set of named constants, which are known as enumeration members, using theEnum class from theenum module.
You create an enumeration by subclassing theEnum class and defining class attributes to represent the enumeration members. You can access and use these members in your code by referencing them through the enumeration class.
UsingEnum provides several advantages, including grouping related constants, improving code readability, ensuring constant values, and enabling type safety by differentiating similar values across different enums.
Enum is the base class for creating enumerations,IntEnum allows members to behave like integers,IntFlag enables bitwise operations on members, andFlag also supports bitwise operations but doesn’t inherit fromint.
Yes, you can iterate over enum members using afor loop, and you can access all members, including aliases, by iterating over the.__members__ attribute.
Recommended Course
🐍 Python Tricks 💌
Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

AboutLeodanis Pozo Ramos
Leodanis is a self-taught Python developer, educator, and technical writer with over 10 years of experience.
» More about LeodanisMasterReal-World Python Skills With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
MasterReal-World Python Skills
With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
What Do You Think?
What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.
Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.
Looking for a real-time conversation? Visit theReal Python Community Chat or join the next“Office Hours” Live Q&A Session. Happy Pythoning!
Keep Learning
Related Topics:intermediatedata-structurespythonstdlib
Related Learning Paths:
Related Courses:
Related Tutorials:
Keep reading Real Python by creating a free account or signing in:
Already have an account?Sign-In






