Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
239: The enum Module in Depth
Imagine you're designing a system for a coffee shop. You have specific drink sizes: Small, Medium, and Large. Now, you could represent these as strings ("small", "medium", "large") or as integers (1, 2, 3). But here is the problem: strings are prone to typos—one developer writes "Small" and another writes "small", and suddenly your order logic is broken. Integers are worse; six months from now, you'll look at if size == 2: and have no idea what "2" actually means without hunting down a comment in another file.
An Enum (short for Enumeration) is like a physical, printed menu. It defines a fixed set of named constants that cannot be changed. Instead of guessing what a value means, you refer to a specific "menu item."
Replacing Magic Strings with Symbolic Names
In Python, we use the enum module to create these sets. I usually recommend Enum as your starting point. It creates a class where the members are constants. Let's map our coffee sizes to this concept:
from enum import Enum
class CoffeeSize(Enum):
SMALL = 1
MEDIUM = 2
LARGE = 3
# Now we use it like this
my_order = CoffeeSize.MEDIUM
if my_order == CoffeeSize.MEDIUM:
print("Coming right up!")
Notice that CoffeeSize.MEDIUM is not just the number 2; it's a member of the CoffeeSize class. It has a name ("MEDIUM") and a value (2). This separation is what saves your skin during debugging.
Letting Python Handle the Numbering
I'll be honest: most of the time, I don't actually care if "Small" is 1 or 100. I just care that it's unique. Manually assigning integers is tedious and error-prone. That's where auto() comes in. It tells Python, "Just give this a unique value; I don't care what it is."
from enum import Enum, auto
class OrderStatus(Enum):
PENDING = auto()
PREPARING = auto()
READY = auto()
DELIVERED = auto()
This keeps your code clean and prevents the "off-by-one" errors that happen when you're manually numbering a list of twenty different states.
The Danger of Duplicate Values
By default, Enum allows different names to have the same value. This is called "aliasing." While sometimes useful, it's usually a mistake. If you accidentally assign SMALL = 1 and TINY = 1, Python won't complain, but your logic might get weird.
To stop this, use the @unique decorator. It forces Python to raise a ValueError if you accidentally reuse a value.
from enum import Enum, auto, unique
@unique
class CoffeeSize(Enum):
SMALL = 1
MEDIUM = 2
LARGE = 1 # This will now trigger a ValueError immediately!
When You Actually Need Integers: IntEnum
Standard Enum members aren't integers; they are objects. If you try to compare CoffeeSize.SMALL == 1, it will return False. But what if you're interfacing with a legacy database or a JSON API that expects actual numbers?
That's where IntEnum comes in. It behaves exactly like an Enum, but it also inherits from int, making it compatible with integer comparisons.
from enum import IntEnum
class Priority(IntEnum):
LOW = 1
HIGH = 2
# This works with IntEnum, but would fail with a regular Enum
if Priority.LOW == 1:
print("This is low priority.")
Iterating and Inspecting Your Enums
One of the best things about enums is that they are iterable. I often use this to populate dropdown menus in UI code or to validate input from a user.
for size in CoffeeSize:
print(f"Option: {size.name} (Value: {size.value})")
You can also look up a member by its value using CoffeeSize(1), which returns CoffeeSize.SMALL. This is incredibly handy when converting raw data from a database back into a typed Python object.
📋 Practical Task
Build a Document Approval Workflow State Machine
You are building a system for a legal firm to track the status of contracts. You need to implement a state machine that ensures documents move through a specific lifecycle.
Requirements:
- Create an Enum called
DocumentStateusing the@uniquedecorator. - The states should be:
DRAFT,UNDER_REVIEW,APPROVED, andREJECTED. Useauto()for the values. - Write a function called
transition_document(current_state, action). - The function should take the current
DocumentStateand a stringaction(e.g., "submit", "approve", "reject"). - Logic:
- If state is
DRAFTand action is"submit"$\rightarrow$ returnUNDER_REVIEW. - If state is
UNDER_REVIEWand action is"approve"$\rightarrow$ returnAPPROVED. - If state is
UNDER_REVIEWand action is"reject"$\rightarrow$ returnREJECTED. - For any other invalid combination, return the
current_stateunchanged.
- If state is
Test your code: Start a document in DRAFT, transition it to UNDER_REVIEW, and then transition it to APPROVED. Print the .name of the state at each step.
There are no comments for now.