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

ApproachWhen to use it
Pure dynamicShort scripts, prototypes, highly polymorphic logic
Annotations + mypy/PyrightInternal 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