10 Python Anti-Patterns: Stop Writing Over-Engineered Chaos
The Trap of Clever Python Code
Control Flow and Structural Missteps
A common habit is using exceptions for control flow. This stems from the try-except blocks to handle standard logic—like switching to a backup API—makes code unreadable and slow. Exceptions are for exceptional circumstances, not for expected logical branches.
Similarly, many developers coming from .py file.
# Anti-pattern: Using classes for simple functions
class DataUtils:
@staticmethod
def clean_text(text):
return text.strip().lower()
# Better: Just use a module-level function
def clean_text(text):
return text.strip().lower()

The Dangers of Surprising Behavior
Overriding dunder methods—like __new__—to return unexpected object types is a recipe for debugging nightmares. If you call an initializer for a Payment class, you expect a Payment object, not a sub-instance of StripePayment or PayPalPayment. This "magic" breaks the expectations of linters and teammates alike. Instead of complex inheritance magic, use a simple dictionary or an
We also see this with custom decorators used solely for dependency injection. Wrapping a main function in a three-layer-deep nested callable just to pass a configuration file adds immense cognitive load. Often, a direct function call to load_config() is the cleaner, more transparent choice.
Over-Engineering and Inappropriate Intimacy
Design patterns like the Abstract Factory or Visitor pattern are powerful tools, but they often lead to over-engineering. If a dictionary and two functions can solve the problem, creating five classes and an abstract base class is a waste of time. The most important principle is keeping it simple.
Furthermore, keep an eye out for Inappropriate Intimacy, where two classes know too much about each other’s internal structures. If an exporter function has to know the exact attribute names of a Report object, they are too tightly coupled.
# Improved Abstraction with Protocols
from typing import Protocol
class Exportable(Protocol):
def to_csv(self) -> str: ...
def export_to_csv(item: Exportable):
print(item.to_csv())
By using Report or a Budget, as long as the object follows the contract.
Prerequisites
To get the most out of these refactoring techniques, you should be comfortable with basic
Key Libraries & Tools
- Python Standard Library: Specifically
os.walk,json, andtyping.Protocolfor structural integrity. - Lokalise: An AI-powered platform used to manage translations and move hardcoded strings out of the UI.
- Pydantic / Click / Typer: Mature libraries that replace the need for custom-built boilerplate for data validation and CLI interfaces.
Syntax Notes
- Dunder Methods: Special methods like
__call__allow objects to be treated as functions, which can be a cleaner alternative to complex decorators. - Type Annotations: While ignored at runtime, these are vital for static analysis and making code self-documenting.
- Wildcard Imports: Using
from module import *is an anti-pattern because it pollutes the namespace and obscures the source of functions.
Practical Examples
Instead of hardcoding every UI string in a
Tips & Gotchas
Avoid the urge to "reinvent the wheel." os.walk already exists. The ecosystem is your greatest asset; leverage mature third-party libraries rather than building custom, unproven solutions for standard problems.