Advanced type annotations using Python's TypeVar
Type annotations are very common now in the Python world.
The typing
module has a lot of powerful features, and in this article we'll explore TypeVar
, which is essential for annotating certain functions correctly.
Simple annotations
A simple type annotated function is shown below:
def get_message(name: str) -> str:
return f'Hello {name}'
We pass in a string, and return a string - nice and easy.
TypeVar
Advanced annotations using There are some situations where we have to get more creative with our type annotations. Consider the function below, which doubles the number we pass into it:
def double(value: int | float | decimal.Decimal):
return value * 2
Several value types are allowed (int
, float
and Decimal
). We could add the following return type:
def double(
value: int | float | decimal.Decimal
) -> int | float | decimal.Decimal:
return value * 2
But when you think about it, it doesn't really make sense. When we pass in an int
, we should get an int
returned. What this type annotation is saying is that when we pass in an int
, then we could get back an int
, float
or Decimal
.
This is where TypeVar
comes in. It allows us to do this:
import decimal
from typing import TypeVar
Number = TypeVar("Number", int, float, decimal.Decimal)
def double(value: Number) -> Number:
return value * 2
This tells static analysis tools like mypy
and Pylance
that the type returned by the function is the same as the type which was passed in.
It also tells the type checker that values other than int
, float
and Decimal
aren't allowed:
double("hello") # error
Piccolo uses TypeVar
extensively - without it, it would be impossible to provide correct types for certain functions. Give it a go!
Posted on: 7 Jan 2023
Have any comments or feedback on this post? Chat with us on GitHub.