Mypy vs Python Dynamic Typing: Key Differences
Python fundamentally operates as a dynamically typed language where data types are enforced at runtime, but modern workflows increasingly incorporate static type checkers like Mypy to verify types before code execution. While Python's runtime dynamically associates types with values rather than variables, static type checkers analyze type annotations ahead of time without executing the program. Understanding the differences between these two approaches clarifies how Python handles variable types, when errors are surfaced, and how developers can build safer, more maintainable codebases without sacrificing runtime flexibility.
Python’s Dynamic Runtime Typing
By default, Python evaluates types dynamically at runtime. Variables do not have intrinsic types; instead, they are merely references to objects in memory that hold their own types.
- Runtime Enforcement: Python does not verify whether
a function call or operation is valid until the interpreter executes
that specific line of code. If an invalid operation occurs—such as
attempting to concatenate an integer to a string—a
TypeErroris raised during execution. - Duck Typing: Python relies on the philosophy of "duck typing" ("if it walks like a duck and quacks like a duck, it's a duck"). An operation succeeds as long as the object implements the required methods or behaviors at the moment of execution, regardless of its explicit class hierarchy.
- Flexibility vs. Risk: This model allows for rapid prototyping and flexible code, but it introduces the risk of edge-case bugs that only reveal themselves when specific code paths are executed in production.
Static Type Checkers (Mypy)
Mypy is a static analysis tool that inspects Python source code using type hints introduced in PEP 484. It checks for type consistency before the code is ever run.
- Ahead-of-Time Verification: Mypy scans the abstract syntax tree of your codebase to ensure that function arguments, return types, and variable assignments match their declared annotations.
- Zero Runtime Overhead: Python's interpreter completely ignores type annotations during execution. Consequently, running Mypy does not change how your code behaves at runtime, nor does it affect execution speed.
- Predictive Bug Prevention: Because Mypy analyzes
the entire codebase statically, it uncovers invalid operations, missing
attributes, and
Nonereference errors across unreachable or rarely used code branches prior to deployment.
Key Differences at a Glance
| Feature | Dynamic Runtime Typing | Static Typing with Mypy |
|---|---|---|
| Execution Phase | Happens while the program runs. | Happens before runtime (via CLI or CI/CD). |
| Error Discovery | Errors surface only when a specific line executes. | Errors surface globally during analysis. |
| Performance Impact | Slight overhead during runtime type checking/resolution. | Zero runtime overhead; purely developer-facing. |
| Type Mutability | Variables can be reassigned to completely different types. | Flags reassignments that violate initial type bounds. |
| Strictness | Permissive until an unsupported operation occurs. | Strict based on defined annotations and configuration. |
How They Work Together: Gradual Typing
Mypy does not replace Python's dynamic runtime; instead, it
complements it through a system called gradual typing.
Developers can leave parts of a codebase dynamic for rapid iteration
while applying strict static typing to critical business logic. If an
unannotated function is passed to Mypy, it treats the values dynamically
(typed as Any), allowing teams to incrementally adopt type
safety without rewriting existing dynamic code.