- Author: Amin Boulouma, Software Engineer
- Github source code: https://github.com/aminblm/ai_systems_design_from_scratch
- Engineering Blog: https://aminblm.github.io/ai_systems_design_from_scratch/blog/
Modern Python: Why dict Beats Dict for Enterprise Scalability
For years, Python developers relied on the typing module to annotate code, writing from typing import List, Dict. While this was necessary in older versions, modern Python (3.9+) has deprecated this approach in favor of using built-in primitives. If you are still importing these types, you are writing legacy code that is harder to read, slower to import, and unnecessarily complex.
The Problem: The typing module was originally a stop-gap. Using Dict[str, int] requires an import, adds cognitive overhead for new developers, and creates a subtle drift between runtime behavior and static analysis. It’s time to embrace the native language features.
The Glossary
- Primitive: The basic building blocks of the language, like
list,dict, andtuple. - Type Hint: A helpful label that tells other developers (and code-checking tools) what kind of data a function expects.
- Import: Telling your code to go fetch a tool from a different library before you can use it.
- Overhead: Extra work the computer has to do, like checking a library before it even starts your actual work.
- Legacy Code: Older code that works, but isn’t written the way we do things today.
Why We Prioritize Built-ins Over typing
We choose native primitives (e.g., dict, list, tuple) because they are always available. They remove the need for boiler-plate imports, speed up startup times, and keep the namespace clean. In enterprise-grade systems, minimizing external dependencies—even internal ones—is crucial for stability and maintainability.
Implementation
Simple Example: The Old Way vs. The Modern Way
# THE OLD WAY (Pre-Python 3.9)
from typing import Dict, List
def process_data(data: Dict[str, List[int]]):
pass
# THE MODERN WAY (Python 3.9+)
def process_data(data: dict[str, list[int]]):
pass
Complex Example: Production-Grade Signature
class ConfigurationManager:
"""Production-grade: Using native generics for type-safe config maps."""
def __init__(self, settings: dict[str, int | str]):
self._settings = settings
def get_value(self, key: str) -> int | str | None:
"""
Uses native pipe '|' for unions instead of Union[A, B].
"""
return self._settings.get(key)
# Usage
config = ConfigurationManager({"timeout": 30, "mode": "debug"})
Quick Reference: Strategy Selection
| Old Way | New Way (Primitive) | When to use |
|---|---|---|
from typing import Dict |
dict |
Everywhere in Python 3.9+ |
from typing import List |
list |
Everywhere in Python 3.9+ |
from typing import Union |
| (Pipe) |
Developer Checklist
- Have I removed
from typing import ...for standard collections? - Are my function signatures using the
|operator for unions? - Is my Python runtime version 3.9 or higher? (If not, upgrade!)
- Have I updated my type-checking tools (mypy/pyright) to support modern syntax?
Takeaways
- Less is More: Native primitives are cleaner and faster.
- Standardize: Stop carrying legacy habits into new projects; standardizing on primitives makes your code more “Pythonic.”
- Future-Proof: Using modern syntax signals to the community that your project is current and maintained.
Counter-intuitive insight: The fastest code is often the code you don’t have to import. By sticking to language primitives, you bypass unnecessary namespace lookups and provide a clearer, more predictable contract for anyone reading your code.