Skip to content

Type annotation

Read Python type hints, distinguish a declared type from an enforced check, and see why an annotated function can still repeat a string at runtime.

A type annotation is an explicit statement of an intended data type, attached to a variable, function parameter or return value. In Python, annotations commonly serve as type hints for readers and tools. They make a quant function's interface easier to inspect, but do not by themselves validate incoming prices or convert text into numbers.

Read the signature

In def double(x: float) -> float:, x: float describes the expected argument type and -> float describes the expected return type. The arrow does not return a value; the return statement in the body does that. A variable can also carry an annotation, as in exposure: float = 3.0. A collection hint such as returns: list[float] describes a list whose elements are intended to be floats.
These are declarations of intent. Python's runtime does not automatically enforce them. The official typing documentation makes that distinction explicit.

The hint does not change multiplication

Run this in ordinary Python, without a validation wrapper:

def double(x: float) -> float: """Return twice the supplied value.""" return x * 2 print(double(3.0)) # 6.0 print(repr(double("3"))) # '33'
The first call performs numeric multiplication. In the second, "3" is a string, and multiplying a string by an integer repeats it. The function therefore returns '33', despite the float return annotation. Neither annotation requests an implicit call to float().
The signature double(x: float) to float states intended types. Its body x times 2 returns float 6.0 for 3.0 and string 33 for string 3 when no separate validation runs.
The signature double(x: float) to float states intended types. Its body x times 2 returns float 6.0 for 3.0 and string 33 for string 3 when no separate validation runs.
The annotation states the intended contract. The supplied object and the function body determine these runtime results.
A market-data pipeline can encounter the same boundary when a price arrives as text. Adding price: float to a downstream function documents its expectation; parsing and validating the incoming record still need an explicit policy.

Three separate jobs

MechanismWhat it contributes
Type annotationStates the intended type in the source. Editors and other tools can inspect it.
Static type checkingA separate checker compares the code's uses with its type information. With suitable checking enabled, it can report that the string call conflicts with the declared parameter type before that call runs.
Runtime validationExecutable guards or a validation library check actual supplied values and reject or transform them according to a chosen rule.

A checker does not run merely because annotations are present. The example above demonstrates runtime behaviour; it is not a checker transcript.

Types also leave financial meaning unspecified. A float annotation alone does not state whether a rate is annual or monthly, whether a return is decimal or percent, or which unit of measure applies. Negative values, infinity and NaN can all be floats. State financial conventions in names and documentation, and enforce the relevant value restrictions at the data boundary.

A version detail for introspection

Annotation evaluation is distinct from checking a function's arguments. In Python 3.14, annotations on functions, classes and modules are lazily evaluated by default. Code that inspects annotations should follow the version's documented introspection rules rather than assume every annotation expression runs when the function is defined. This change does not make type hints enforce runtime types. The example was executed with Python 3.14.4.

For a worked function contract with actual input guards, read Functions in Python Foundations, free with sign-in.

Sources

Last updated
  • python
  • type hints
  • software quality
← Back to the glossaryBack to top ↑