Python’s enum
module offers the Enum
class, which lets you create enumeration varieties. To create your individual enumerations, you possibly can both subclass Enum
or use its purposeful API. Each choices will allow you to outline a set of associated constants as enum members.
Within the following sections, you’ll learn to create enumerations in your code utilizing the Enum
class. You’ll additionally learn to set robotically generated values in your enums and tips on how to create enumerations containing alias and distinctive values. To kick issues off, you’ll begin by studying tips on how to create an enumeration by subclassing Enum
.
Creating Enumerations by Subclassing Enum
The enum
module defines a general-purpose enumeration kind with iteration and comparison capabilities. You should use this kind to create units of named constants that you need to use to exchange literals of frequent information varieties, resembling numbers and strings.
A basic instance of when you need to use an enumeration is when you want to create a set of enumerated constants representing the times of the week. Every day can have a symbolic title and a numeric worth between 1
and 7
, inclusive.
Right here’s how one can create this enumeration through the use of Enum
as your superclass or guardian class:
>>> from enum import Enum
>>> class Day(Enum):
... MONDAY = 1
... TUESDAY = 2
... WEDNESDAY = 3
... THURSDAY = 4
... FRIDAY = 5
... SATURDAY = 6
... SUNDAY = 7
...
>>> checklist(Day)
[
<Day.MONDAY: 1>,
<Day.TUESDAY: 2>,
<Day.WEDNESDAY: 3>,
<Day.THURSDAY: 4>,
<Day.FRIDAY: 5>,
<Day.SATURDAY: 6>,
<Day.SUNDAY: 7>
]
Your Day
class is a subclass of Enum
. So, you possibly can name Day
an enumeration, or simply an enum. Day.MONDAY
, Day.TUESDAY
, and the like are enumeration members, also called enum members, or simply members. Every member will need to have a worth, which must be fixed.
As a result of enumeration members should be constants, Python doesn’t can help you assign new values to enum members at runtime:
>>> Day.MONDAY = 0
Traceback (most up-to-date name final):
...
AttributeError: Can't reassign members.
>>> Day
<enum 'Day'>
>>> # Rebind Day
>>> Day = "Monday"
>>> Day
'Monday'
In case you attempt to change the worth of an enum member, then you definitely get an AttributeError
. In contrast to member names, the title containing the enumeration itself isn’t a continuing however a variable. So, it’s doable to rebind this title at any second throughout your program’s execution, however you need to keep away from doing that.
Within the instance above, you’ve reassigned Day
, which now holds a string quite than the unique enumeration. By doing this, you’ve misplaced the reference to the enum itself.
Typically, the values mapped to members are consecutive integer numbers. Nevertheless, they are often of any kind, together with user-defined varieties. On this instance, the worth of Day.MONDAY
is 1
, the worth of Day.TUESDAY
is 2
, and so forth.
Word: You might have seen that the members of Day
are capitalized. Right here’s why:
As a result of Enums are used to characterize constants we advocate utilizing UPPER_CASE names for enum members… (Source)
You may consider enumerations as collections of constants. Like lists, tuples, or dictionaries, Python enumerations are additionally iterable. That’s why you need to use list()
to show an enumeration right into a checklist
of enumeration members.
The members of a Python enumeration are cases of the container enumeration itself:
>>> from enum import Enum
>>> class Day(Enum):
... MONDAY = 1
... TUESDAY = 2
... WEDNESDAY = 3
... THURSDAY = 4
... FRIDAY = 5
... SATURDAY = 6
... SUNDAY = 7
...
>>> kind(Day.MONDAY)
<enum 'Day'>
>>> kind(Day.TUESDAY)
<enum 'Day'>
You shouldn’t confuse a customized enum class like Day
with its members: Day.MONDAY
, Day.TUESDAY
, and so forth. On this instance, the Day
enum kind is a hub for enumeration members, which occur to be of kind Day
.
It’s also possible to use the idiom based mostly on range()
to construct enumerations:
>>> from enum import Enum
>>> class Season(Enum):
... WINTER, SPRING, SUMMER, FALL = vary(1, 5)
...
>>> checklist(Season)
[
<Season.WINTER: 1>,
<Season.SPRING: 2>,
<Season.SUMMER: 3>,
<Season.FALL: 4>
]
On this instance, you employ vary()
with the begin
and cease
offsets. The begin
offset permits you to present the quantity that begins the vary, whereas the cease
offset defines the quantity at which the vary will cease producing numbers.
Regardless that you employ the class
syntax to create enumerations, they’re particular lessons that differ from regular Python lessons. In contrast to common lessons, enums:
You need to remember all these refined variations if you begin creating and dealing with your individual enumerations in Python.
Typically, the members of an enumeration take consecutive integer values. Nevertheless, in Python, the values of members will be of any kind, together with user-defined varieties. For instance, right here’s an enumeration of faculty grades that makes use of non-consecutive numeric values in descending order:
>>> from enum import Enum
>>> class Grade(Enum):
... A = 90
... B = 80
... C = 70
... D = 60
... F = 0
...
>>> checklist(Grade)
[
<Grade.A: 90>,
<Grade.B: 80>,
<Grade.C: 70>,
<Grade.D: 60>,
<Grade.F: 0>
]
This instance exhibits that Python enums are fairly versatile and can help you use any significant worth for his or her members. You may set the member values in keeping with the intent of your code.
It’s also possible to use string values in your enumeration members. Right here’s an instance of a Dimension
enumeration that you need to use in a web-based retailer:
>>> from enum import Enum
>>> class Dimension(Enum):
... S = "small"
... M = "medium"
... L = "giant"
... XL = "further giant"
...
>>> checklist(Dimension)
[
<Size.S: 'small'>,
<Size.M: 'medium'>,
<Size.L: 'large'>,
<Size.XL: 'extra large'>
]
On this instance, the worth related to every dimension holds an outline that may assist you to and different builders perceive the that means of your code.
It’s also possible to create enumerations of Boolean values. On this case, the members of your enumeration can have solely two values:
>>> from enum import Enum
>>> class SwitchPosition(Enum):
... ON = True
... OFF = False
...
>>> checklist(SwitchPosition)
[<SwitchPosition.ON: True>, <SwitchPosition.OFF: False>]
>>> class UserResponse(Enum):
... YES = True
... NO = False
...
>>> checklist(UserResponse)
[<UserResponse.YES: True>, <UserResponse.NO: False>]
These two examples present how you need to use enumerations so as to add further context to your code. Within the first instance, anybody studying your code will know that the code emulates a change object with two doable states. This extra info extremely improves your code’s readability.
It’s also possible to outline an enumeration with heterogeneous values:
>>> from enum import Enum
>>> class UserResponse(Enum):
... YES = 1
... NO = "No"
...
>>> UserResponse.NO
<UserResponse.NO: 'No'>
>>> UserResponse.YES
<UserResponse.YES: 1>
Nevertheless, this apply makes your code inconsistent from a type safety perspective. Due to this fact, it’s not really helpful apply. Ideally, it could assist in the event you had values of the identical information kind, which is per the thought of grouping related, associated constants in enumerations.
Lastly, you too can create empty enumerations:
>>> from enum import Enum
>>> class Empty(Enum):
... go
...
>>> checklist(Empty)
[]
>>> class Empty(Enum):
... ...
...
>>> checklist(Empty)
[]
>>> class Empty(Enum):
... """Empty enumeration for such and such functions."""
...
>>> checklist(Empty)
[]
On this instance, Empty
represents an empty enumeration as a result of it doesn’t outline any member constants. Word that you need to use the pass
assertion, the Ellipsis
literal (...
), or a class-level docstring to create empty enumerations. This final method may help you enhance the readability of your code by offering further context within the docstring.
Now, why would you want to outline an empty enumeration anyway? Empty enumerations can come in useful when you want to construct a hierarchy of enum lessons to reuse performance via inheritance.
Think about the next instance:
>>> from enum import Enum
>>> import string
>>> class BaseTextEnum(Enum):
... def as_list(self):
... strive:
... return checklist(self.worth)
... besides TypeError:
... return [str(self.value)]
...
>>> class Alphabet(BaseTextEnum):
... LOWERCASE = string.ascii_lowercase
... UPPERCASE = string.ascii_uppercase
...
>>> Alphabet.LOWERCASE.as_list()
['a', 'b', 'c', 'd', ..., 'x', 'y', 'z']
On this instance, you create BaseTextEnum
as an enumeration with no members. You may solely subclass a customized enumeration if it doesn’t have members, so BaseTextEnum
qualifies. The Alphabet
class inherits out of your empty enumeration, which implies you can entry the .as_list()
technique. This technique converts the worth of a given member into an inventory.
Creating Enumerations With the Practical API
The Enum
class offers a functional API that you need to use to create enumerations with out utilizing the same old class syntax. You’ll simply have to name Enum
with acceptable arguments such as you’d do with a function or another callable.
This purposeful API resembles the best way wherein the namedtuple()
manufacturing facility perform works. Within the case of Enum
, the purposeful signature has the next kind:
Enum(
worth,
names,
*,
module=None,
qualname=None,
kind=None,
begin=1
)
From this signature, you possibly can conclude that Enum
wants two positional arguments, worth
and names
. It may well additionally take as much as 4 optional and keyword-only arguments. These arguments are module
, qualname
, kind
, and begin
.
Right here’s a desk that summarizes the content material and that means of every argument within the signature of Enum
:
Argument | Description | Required |
---|---|---|
worth |
Holds a string with the title of the brand new enumeration class | Sure |
names |
Supplies names for the enumeration members | Sure |
module |
Takes the title of the module that defines the enumeration class | No |
qualname |
Holds the placement of the module that defines the enumeration class | No |
kind |
Holds a category for use as the primary mixin class | No |
begin |
Takes the beginning worth from the enumeration values will start | No |
To supply the names
argument, you need to use the next objects:
- A string containing member names separated both with areas or commas
- An iterable of member names
- An iterable of name-value pairs
The module
and qualname
arguments play an essential position when you want to pickle and unpickle your enumerations. If module
isn’t set, then Python will try to search out the module. If it fails, then the category is not going to be picklable. Equally, if qualname
isn’t set, then Python will set it to the global scope, which can trigger your enumerations to fail unpickling in some conditions.
The kind
argument is required if you wish to present a mixin class in your enumeration. Utilizing a mixin class can present your customized enum with new performance, resembling prolonged comparability capabilities, as you’ll be taught within the part about mixing enumerations with other data types.
Lastly, the begin
argument offers a approach to customise the preliminary worth of your enumerations. This argument defaults to 1
quite than to 0
. The rationale for this default worth is that 0
is fake in a Boolean sense, however enum members consider to True
. Due to this fact, ranging from 0
would appear shocking and complicated.
More often than not, you’ll simply use the primary two arguments to Enum
when creating your enumerations. Right here’s an instance of making an enumeration of frequent HTTP methods:
>>> from enum import Enum
>>> HTTPMethod = Enum(
... "HTTPMethod", ["GET", "POST", "PUSH", "PATCH", "DELETE"]
... )
>>> checklist(HTTPMethod)
[
<HTTPMethod.GET: 1>,
<HTTPMethod.POST: 2>,
<HTTPMethod.PUSH: 3>,
<HTTPMethod.PATCH: 4>,
<HTTPMethod.DELETE: 5>
]
This name to Enum
returns a brand new enumeration known as HTTPMethod
. To supply the member names, you employ an inventory of strings. Every string represents an HTTP technique. Word that the member values are robotically set to consecutive integer numbers ranging from 1
. You may change this preliminary worth utilizing the begin
argument.
Word that defining the above enumerations with the category syntax will produce the identical outcome:
>>> from enum import Enum
>>> class HTTPMethod(Enum):
... GET = 1
... POST = 2
... PUSH = 3
... PATCH = 4
... DELETE = 5
...
>>> checklist(HTTPMethod)
[
<HTTPMethod.GET: 1>,
<HTTPMethod.POST: 2>,
<HTTPMethod.PUSH: 3>,
<HTTPMethod.PATCH: 4>,
<HTTPMethod.DELETE: 5>
]
Right here, you employ the category syntax to outline the HTTPMethod
enum. This instance is totally equal to the earlier one, as you possibly can conclude from the output of checklist()
.
Utilizing both the category syntax or the purposeful API to create your enumeration is your determination and can largely rely in your style and concrete circumstances. Nevertheless, if you wish to create enumerations dynamically, then the purposeful API will be your solely choice.
Think about the next instance, the place you create an enum with user-provided members:
>>> from enum import Enum
>>> names = []
>>> whereas True:
... title = enter("Member title: ")
... if title in "q", "Q":
... break
... names.append(title.higher())
...
Member title: YES
Member title: NO
Member title: q
>>> DynamicEnum = Enum("DynamicEnum", names)
>>> checklist(DynamicEnum)
[<DynamicEnum.YES: 1>, <DynamicEnum.NO: 2>]
This instance is a bit bit excessive as a result of creating any object out of your person’s enter is sort of a dangerous apply, contemplating you can’t predict what the person will enter. Nevertheless, the instance is meant to indicate that the purposeful API is the best way to go when you want to create enumerations dynamically.
Lastly, if you want to set customized values in your enum members, then you need to use an iterable of name-value pairs as your names
argument. Within the instance beneath, you employ an inventory of name-value tuples to initialize all of the enumeration members:
>>> from enum import Enum
>>> HTTPStatusCode = Enum(
... worth="HTTPStatusCode",
... names=[
... ("OK", 200),
... ("CREATED", 201),
... ("BAD_REQUEST", 400),
... ("NOT_FOUND", 404),
... ("SERVER_ERROR", 500),
... ],
... )
>>> checklist(HTTPStatusCode)
[
<HTTPStatusCode.OK: 200>,
<HTTPStatusCode.CREATED: 201>,
<HTTPStatusCode.BAD_REQUEST: 400>,
<HTTPStatusCode.NOT_FOUND: 404>,
<HTTPStatusCode.SERVER_ERROR: 500>
]
Offering an inventory of name-value tuples such as you did above makes it doable to create the HTTPStatusCode
enumeration with customized values for the members. On this instance, in the event you didn’t wish to use an inventory of name-value tuples, then you can additionally use a dictionary that maps names to values.
Constructing Enumerations From Computerized Values
Python’s enum
module offers a handy perform known as auto()
that permits you to set computerized values in your enum members. This perform’s default habits is to assign consecutive integer values to members.
Right here’s how auto()
works:
>>> from enum import auto, Enum
>>> class Day(Enum):
... MONDAY = auto()
... TUESDAY = auto()
... WEDNESDAY = 3
... THURSDAY = auto()
... FRIDAY = auto()
... SATURDAY = auto()
... SUNDAY = 7
...
>>> checklist(Day)
[
<Day.MONDAY: 1>,
<Day.TUESDAY: 2>,
<Day.WEDNESDAY: 3>,
<Day.THURSDAY: 4>,
<Day.FRIDAY: 5>,
<Day.SATURDAY: 6>,
<Day.SUNDAY: 7>
]
You have to name auto()
as soon as for every computerized worth that you simply want. It’s also possible to mix auto()
with concrete values, identical to you probably did with Day.WEDNESDAY
and Day.SUNDAY
on this instance.
By default, auto()
assigns consecutive integer numbers to every goal member ranging from 1
. You may tweak this default habits by overriding the ._generate_next_value_()
technique, which auto()
makes use of below the hood to generate the automated values.
Right here’s an instance of how to do that:
>>> from enum import Enum, auto
>>> class CardinalDirection(Enum):
... def _generate_next_value_(title, begin, rely, last_values):
... return title[0]
... NORTH = auto()
... SOUTH = auto()
... EAST = auto()
... WEST = auto()
...
>>> checklist(CardinalDirection)
[
<CardinalDirection.NORTH: 'N'>,
<CardinalDirection.SOUTH: 'S'>,
<CardinalDirection.EAST: 'E'>,
<CardinalDirection.WEST: 'W'>
]
On this instance, you create an enumeration of Earth’s cardinal directions wherein values are robotically set to strings containing the primary character of every member’s title. Word that you could present your overridden model of ._generate_next_value_()
earlier than defining any members. That’s as a result of the members shall be constructed by calling the strategy.
Creating Enumerations With Aliases and Distinctive Values
You may create enumerations wherein two or extra members have the identical fixed worth. The redundant members are referred to as aliases and will be helpful in some conditions. For instance, say that you’ve an enum containing a set of working methods (OS), like within the following code:
>>> from enum import Enum
>>> class OperatingSystem(Enum):
... UBUNTU = "linux"
... MACOS = "darwin"
... WINDOWS = "win"
... DEBIAN = "linux"
...
>>> # Aliases aren't listed
>>> checklist(OperatingSystem)
[
<OperatingSystem.UBUNTU: 'linux'>,
<OperatingSystem.MACOS: 'darwin'>,
<OperatingSystem.WINDOWS: 'win'>
]
>>> # To entry aliases, use __members__
>>> checklist(OperatingSystem.__members__.gadgets())
[
('UBUNTU', <OperatingSystem.UBUNTU: 'linux'>),
('MACOS', <OperatingSystem.MACOS: 'darwin'>),
('WINDOWS', <OperatingSystem.WINDOWS: 'win'>),
('DEBIAN', <OperatingSystem.UBUNTU: 'linux'>)
]
Linux distributions are thought-about impartial working methods. So, Ubuntu and Debian are each impartial methods with completely different objectives and goal audiences. Nevertheless, they share a standard kernel known as Linux.
The above enumeration maps working methods to their corresponding kernels. This relationship turns DEBIAN
into an alias of UBUNTU
, which can be helpful when you have got code that’s kernel-related together with code that’s particular to a given Linux distribution.
An essential piece of habits to notice within the above instance is that if you iterate over the enumeration straight, aliases aren’t thought-about. In case you ever have to iterate over all of the members, together with aliases, then you want to use .__members__
. You’ll be taught extra about iteration and the .__members__
attribute within the part about iterating through enumerations.
You even have the choice to fully forbid aliases in your enumerations. To do that, you need to use the @unique
decorator from the enum
module:
>>> from enum import Enum, distinctive
>>> @distinctive
... class OperatingSystem(Enum):
... UBUNTU = "linux"
... MACOS = "darwin"
... WINDOWS = "win"
... DEBIAN = "linux"
...
Traceback (most up-to-date name final):
...
ValueError: duplicate values in <enum 'OperatingSystem'>: DEBIAN -> UBUNTU
On this instance, you beautify OperatingSystem
with @distinctive
. If any member worth is duplicated, then you definitely get a ValueError
. Right here, the exception message factors out that DEBIAN
and UBUNTU
share the identical worth, which isn’t allowed.