Developers coming from Java or C++ often bring their OOP baggage to Python, resulting in massive class hierarchies just to pass a single behavior.
# DANGER: Massive boilerplate for a single behavior
class StringFormatter(ABC):
@abstractmethod
def format(self, text: str) -> str: pass
class UpperFormatter(StringFormatter):
def format(self, text: str) -> str: return text.upper()
class LowerFormatter(StringFormatter):
def format(self, text: str) -> str: return text.lower()Fix: If a class only has an __init__ and one other method, it should almost certainly be a function or a closure.
def format_upper(text: str) -> str: return text.upper()
def format_lower(text: str) -> str: return text.lower()The reverse of Pitfall 1 is taking functional programming too far. If a function requires complex, mutable state that evolves over time, using a closure or nonlocal can quickly become unreadable.
# DANGER: Unreadable state management in a closure
def create_complex_processor():
cache = {}
history = []
def process(data):
nonlocal cache, history
# ... 50 lines of complex state mutations ...
pass
return processFix: If you need to manage multiple pieces of mutable state or expose multiple behaviors (methods) interacting with that state, use a class. Python is a multi-paradigm language; classes are excellent for stateful objects.
Because functional strategies are often implemented as module-level functions, it is easy to accidentally rely on global variables.
# DANGER: Global state breaks the Strategy pattern
ACTIVE_DISCOUNT = 0.2
def global_promo(order):
return order.total * ACTIVE_DISCOUNTFix: Strategies must be pure functions relying only on their inputs, or closures that tightly encapsulate their specific state.
When replacing Abstract Base Classes with functions, you lose the IDE's ability to enforce the interface via inheritance.
# DANGER: No signature enforcement
class Order:
def __init__(self, strategy):
self.strategy = strategyFix: You MUST use typing.Callable to restore the signature contract.
from typing import Callable
class Order:
def __init__(self, strategy: Callable[['Order'], float]):
self.strategy = strategy