Q1What's the advantage of replacing "paid" string literal comparison with an Enum?
enum and dataclasses — Named Constants and Data Classes
Replace string literals with Enum, auto-number and sort with IntEnum + auto(), generate boilerplate via @dataclass, and use field(default_factory=list) through examples.
Two modules for making the meaning of values explicit. enum lets you replace string literals scattered through your code with named constants, so typos surface at runtime instead of silently passing through. dataclasses provides a way to define a "class that just holds data" in one line, eliminating the boilerplate of __init__ / __eq__ / __repr__.
Enum — replace string literals with named constants
When string literals like "paid" / "shipped" are scattered through your code, typos like "shippped" slip through until runtime, autocomplete doesn't help, and refactors are fragile. Enum solves this by defining the set of constants as a class — the names you define autocomplete in your editor, typos raise AttributeError immediately, iteration over all members works out of the box, and you get a value safe to use as a dict key or switch label.
# Status strings scattered through the code
if status == "paid":
ship_order(order_id)
elif status == "shippped": # Typo — should be "shipped", but you won't notice until runtime
notify_shipped(order_id)
elif status == "cancelled":
refund(order_id)
from enum import Enum
class OrderStatus(Enum):
PENDING = "pending"
PAID = "paid"
SHIPPED = "shipped"
CANCELLED = "cancelled"
# Member access: each member carries a name and a value
print(OrderStatus.PAID) # OrderStatus.PAID
print(OrderStatus.PAID.name) # 'PAID'
print(OrderStatus.PAID.value) # 'paid'
# Compare with ==
status = OrderStatus.PAID
if status == OrderStatus.PAID:
print("Payment received")
IntEnum and auto — auto-numbering and numeric comparison
IntEnum is an Enum that inherits from int, so members compare and arithmetic-operate as integers. It's the right pick for constants where numeric ordering matters — priorities, levels, ranks. auto() is a function that assigns values automatically in place of writing them by hand: line up auto() calls and they become 1, 2, 3, 4... in definition order.
When the specific numbers don't matter (only the order does), auto() is safer because adding or removing members doesn't require rewriting numbers by hand.
1, 2, 3, .... IntEnum compares with int, so Priority.LOW < Priority.HIGH works — numeric ordering encodes the priority.@dataclass — define a data-holding class in one line
Writing a "plain class with just fields" by hand means defining __init__ to set attributes, __eq__ (the dunder method called for == comparisons) for content equality, __repr__ (the dunder method called by print and the REPL) for a readable display, and so on — the same boilerplate patterns lining up every time. A single @dataclass decorator generates all of those automatically from your type-hinted field declarations.
In other words, just lining up type-hinted attributes builds the whole class — you get the same features for far less code than handwritten classes.
Use default_factory for mutable defaults
Trying to default a list with @dataclass by writing items: list = [] raises a SyntaxError (or deprecation warning). It's a guard against the classic bug of "every instance ends up sharing the same list". Use field(default_factory=list) instead, which creates a fresh empty list per instance, making it safe. Same idea for dict / set: field(default_factory=dict).
Knowledge Check
Answer each question one by one.
Q2What's the advantage of auto() in IntEnum?
Q3Which is the correct way to default a field to a list in @dataclass?