Enums in Python
Enumerations (enums
), provide a powerful way to represent a set of named values. Enums bring clarity, maintainability, and readability to code by replacing numeric or string constants with more meaningful named constants.
Creating Enums
The more systematic of creating enums in Python is by using the Enum
class from the enum
module. For example:
from enum import Enum
class UserRole(Enum):
ADMIN = "admin"
USER = "user"
EDITOR = "editor"
GUEST = "guest"
In the example, UserRole
is an enum
with 4 members: ADMIN
, USER
, EDITOR
, and GUEST
, each associated with a string value.
Enums guarantee unique names by design. Each member of an enum is distinct and attempting to create multiple members with the same name will raise a TypeError
.
from enum import Enum
class UserRole(Enum):
ADMIN = "admin"
USER = "user"
EDITOR = "editor"
GUEST = "guest"
ADMIN = "super user"
#TypeError: Attempted to reuse key: 'ADMIN'
The enum
module also has a decorator, @unique
, that ensures that the enum should have a distinct name and values.
from enum import Enum, unique
@unique
class UserRole(Enum):
ADMIN = "admin"
USER = "user"
EDITOR = "editor"
GUEST = "guest"
ADMIN = "super user"
# TypeError: Attempted to reuse key: 'ADMIN'
If the enum
has duplicate values, then the @unique
decorator generates a ValueError
.
from enum import Enum, unique
@unique
class UserRole(Enum):
ADMIN = "admin"
USER = "user"
EDITOR = "editor"
GUEST = "guest"
S_ADMIN = "admin"
# ValueError: duplicate values found in <enum 'UserRole'>: S_ADMIN -> ADMIN
Accessing Enum Members
We can access the name and value of an enum
member as attributes.
print(UserRole.EDITOR) # UserRole.EDITOR
print(UserRole.EDITOR.value) # Editor
print(UserRole['ADMIN']) # UserRole.ADMIN
Iterating Over Enums
Iterating over enums is straightforward: the loop will output each member of the UserRole
enum.
for role in UserRole:
print(f"Role Name: {role.name} - Value: {role.value}")
Enum Comparisons
Enums supports comparisons both for equality and identity. For example:
print(UserRole.ADMIN == UserRole.ADMIN) # True
print(UserRole.ADMIN is UserRole.ADMIN). # True.
Enums in Function Signatures
From Python's latest versions, 3.10 and above, enums can be used directly in function signatures. This allows us to specify an enum as an argument or return type in function signatures.
- Argument type: Specify the enum type as the type of function parameter.
def get_user_role(role: UserRole):
return role.value
- Return type: Enum can be used as the
return
type of the function.
def my_function() -> UserRole:
return UserRole.ADMIN
Advantages of Using Enums
Improved Code Readability
Enums provide meaningful names for values, making the code more self-explanatory. Instead of using numeric or string constants scattered throughout the code, enum members give descriptive names to the possible values.
Enhanced Maintainability
Enums make code more maintainable by centralizing the definition of possible values. If changes are needed, you only need to modify the enum definition rather than searching and updating occurrences throughout the code.
Prevention of Invalid Values
Enums help prevent the use of invalid values by restricting the choices to the defined enum members. This reduces the chances of introducing bugs due to unexpected values. In our User
role example, if we pass an invalid role value, the program raises a ValueError
.
role = UserRole('s_admin')
# ValueError: 's_admin' is not a valid UserRole
role = UserRole('admin')
# <UserRole.ADMIN: 'admin'>
Expressive Intent
Enums excel in revealing the intention of your code by providing names that distinctly reflect the purpose of the values. This feature contributes to making the code more expressive, enhancing its clarity.
Conclusion
Enums in Python provide a clear and expressive way to represent fixed sets of values. They enhance code readability, prevent bugs by restricting variable values, and contribute to a more maintainable and self-documenting code.