BehavioralPythonverifiedVerified
State Pattern in Python
Allows an object to alter its behavior when its internal state changes, appearing as if the object has changed its class.
How to Implement the State Pattern in Python
1Step 1: Define the state interface using Protocol
from typing import Protocol, Literal
TrafficLightColor = Literal["red", "green", "yellow"]
class TrafficLightState(Protocol):
@property
def color(self) -> TrafficLightColor: ...
def next_state(self) -> "TrafficLightState": ...
def can_go(self) -> bool: ...2Step 2: Implement concrete state classes
class RedLight:
@property
def color(self) -> TrafficLightColor:
return "red"
def next_state(self) -> TrafficLightState:
return GreenLight()
def can_go(self) -> bool:
return False
class GreenLight:
@property
def color(self) -> TrafficLightColor:
return "green"
def next_state(self) -> TrafficLightState:
return YellowLight()
def can_go(self) -> bool:
return True
class YellowLight:
@property
def color(self) -> TrafficLightColor:
return "yellow"
def next_state(self) -> TrafficLightState:
return RedLight()
def can_go(self) -> bool:
return False3Step 3: Create the context that delegates to states
class TrafficLight:
def __init__(self) -> None:
self._state: TrafficLightState = RedLight()
def advance(self) -> None:
self._state = self._state.next_state()
@property
def color(self) -> TrafficLightColor:
return self._state.color
def can_go(self) -> bool:
return self._state.can_go()4Step 4: Cycle through states and observe transitions
light = TrafficLight()
print(light.color, light.can_go()) # red False
light.advance()
print(light.color, light.can_go()) # green True
light.advance()
print(light.color, light.can_go()) # yellow False
light.advance()
print(light.color, light.can_go()) # red False"""Order Lifecycle State Machine using the State pattern."""
import time
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Literal, TYPE_CHECKING
if TYPE_CHECKING:
from typing import Any
OrderStatus = Literal[
"pending", "confirmed", "processing", "shipped",
"delivered", "cancelled", "refunded",
]
# [step] Define the state interface and invalid transition error
class InvalidTransitionError(Exception):
def __init__(self, from_status: str, to_status: str) -> None:
super().__init__(f'Cannot transition from "{from_status}" to "{to_status}"')
class OrderState(ABC):
@property
@abstractmethod
def status(self) -> OrderStatus: ...
def confirm(self, order: "Order") -> None:
raise InvalidTransitionError(self.status, "confirmed")
def process(self, order: "Order") -> None:
raise InvalidTransitionError(self.status, "processing")
def ship(self, order: "Order", tracking_code: str) -> None:
raise InvalidTransitionError(self.status, "shipped")
def deliver(self, order: "Order") -> None:
raise InvalidTransitionError(self.status, "delivered")
def cancel(self, order: "Order", reason: str) -> None:
raise InvalidTransitionError(self.status, "cancelled")
def refund(self, order: "Order") -> None:
raise InvalidTransitionError(self.status, "refunded")
# [step] Implement concrete states with allowed transitions
class PendingState(OrderState):
@property
def status(self) -> OrderStatus:
return "pending"
def confirm(self, order: "Order") -> None:
order.transition_to(ConfirmedState())
def cancel(self, order: "Order", reason: str) -> None:
order.transition_to(CancelledState(reason))
class ConfirmedState(OrderState):
@property
def status(self) -> OrderStatus:
return "confirmed"
def process(self, order: "Order") -> None:
order.transition_to(ProcessingState())
def cancel(self, order: "Order", reason: str) -> None:
order.transition_to(CancelledState(reason))
class ProcessingState(OrderState):
@property
def status(self) -> OrderStatus:
return "processing"
def ship(self, order: "Order", tracking_code: str) -> None:
order.transition_to(ShippedState(tracking_code))
class ShippedState(OrderState):
def __init__(self, tracking_code: str) -> None:
self.tracking_code = tracking_code
@property
def status(self) -> OrderStatus:
return "shipped"
def deliver(self, order: "Order") -> None:
order.transition_to(DeliveredState())
class DeliveredState(OrderState):
@property
def status(self) -> OrderStatus:
return "delivered"
def refund(self, order: "Order") -> None:
order.transition_to(RefundedState())
class CancelledState(OrderState):
def __init__(self, reason: str) -> None:
self.reason = reason
@property
def status(self) -> OrderStatus:
return "cancelled"
class RefundedState(OrderState):
@property
def status(self) -> OrderStatus:
return "refunded"
# [step] Implement the Order context with status history
@dataclass
class OrderItem:
sku: str
quantity: int
price: float
class Order:
def __init__(self, order_id: str, items: list[OrderItem]) -> None:
self._state: OrderState = PendingState()
self.order_id = order_id
self.items = items
self.total = sum(item.price * item.quantity for item in items)
self.status_history: list[dict[str, Any]] = [
{"status": "pending", "at": time.time()}
]
def transition_to(self, state: OrderState, note: str | None = None) -> None:
self._state = state
entry: dict[str, Any] = {"status": state.status, "at": time.time()}
if note:
entry["note"] = note
self.status_history.append(entry)
@property
def status(self) -> OrderStatus:
return self._state.status
def confirm(self) -> None:
self._state.confirm(self)
def process(self) -> None:
self._state.process(self)
def ship(self, tracking_code: str) -> None:
self._state.ship(self, tracking_code)
def deliver(self) -> None:
self._state.deliver(self)
def cancel(self, reason: str) -> None:
self._state.cancel(self, reason)
def refund(self) -> None:
self._state.refund(self)State Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
State Pattern in the Real World
“Think of a traffic light. The light itself (context) doesn't change its wiring, but its active state—red, yellow, or green—completely determines what drivers should do. Each color has its own rules, and the light transitions through states on a timer. Adding a flashing-yellow state only requires defining that state's rules, not rewiring the entire light.”