Typing
In Python, values carry types, not variable declarations. You can layer annotations and checkers (mypy, Pyright) on top without giving up the language’s flexibility.
Dynamic vs static typing
Python is dynamically typed: a name can refer to different types over time. Languages like C++ or Rust are statically typed: types are fixed at compile time.
In Python:
Chargement…
In C++:
Chargement…
It is not just syntax: in Python, the object has a type (int, str, list, …). A variable is only a label pointing at that object.
Duck typing
Python often relies on duck typing: “if it behaves like a duck, it’s a duck.” You care about methods and attributes, not the exact class name.
That keeps APIs flexible. The trade-off is that passing the wrong shape (wrong argument type) may only show up at runtime, sometimes far from the buggy call.
When untyped code hurts
On a tiny isolated function, implicit typing is fine. On a large codebase, you want to avoid “it worked in dev, it breaks in prod” surprises.
Chargement…
Sortie console
Without annotations or a checker, nothing flags the mistake until that code path runs.
Levels of typing in practice
1. Pure dynamic typing
No annotations: everything is resolved at runtime. This is Python’s default.
Chargement…
The trap: + does different things by type (sum, concatenation, …). Without annotations, a column read as str from a CSV can slip through until the backtest runs.
2. Gradual typing (annotations)
Since Python 3, you can annotate parameters and return types. By default, annotations do not change runtime behavior; they help documentation, IDEs (autocomplete, refactors), and static checkers.
Chargement…
With mypy or Pyright, you can check the project before running it:
mypy src/
Example of what a checker might report:
Chargement…
Gradual typing means you can adopt types incrementally: start with critical modules, leave the rest untyped at first.
3. Runtime validation (Pydantic, dataclasses)
For incoming data (HTTP APIs, JSON, config), annotations alone are not enough—you need to validate and coerce at runtime. Pydantic uses annotations to build models that raise clear errors when the shape is wrong.
Chargement…
You stay in dynamic Python, but you reject inconsistent data early—especially at system boundaries (broker feeds, bar files, backtest config).
Summary
| Approach | When to use it |
|---|---|
| Pure dynamic | Short scripts, prototypes, highly polymorphic logic |
| Annotations + mypy/Pyright | Internal libraries, evolving business code |
| Pydantic (or similar) | Structured inputs/outputs (APIs, config, ETL) |
Annotations do not replace tests; they cut down one class of mistakes before execution and make intent obvious to others (and to future you). I mostly use gradual typing on large projects, and I will likely adopt Pydantic more widely in my main research lab.
Resources
- Type system concepts : https://typing.python.org/en/latest/spec/concepts.html
typingmodule documentation : https://docs.python.org/3/library/typing.html