Type annotations#
As we have talked about in earlier lectures, Python is a dynamically language, this means that a variable does not have to have a single type throughout a program. For instance, the following is valid:
x = "42"
print(type(x))
x = 42.0
print(type(x))
x = 42
print(type(x))
<class 'str'>
<class 'float'>
<class 'int'>
In PEP484 (Python Enchancement Proposal 484) it was suggested to add type hints to Python, allowing users to annotate their code with hints at to what type an input or output variable of a function should have. However, these annotations are not checked at run-time, and third-party libraries such as mypy exist for static type-checking.
We can annotate a variable with a type hint by using a “colon” (:
)
a: int # Declare that we expect a to be an integer
b: int = 5 # Declare that we expect an integer and set it to 5
However, as the language is not statically typed, we can still assign other types to the variables
a = "s" # No error
b = [11.0, 15.0, 13.0] # No error
We can use type-hints for all built in types in Python
c: list[str] = ["a", "b", "c"]
For older versions of Python (<3.9), one have to import List
from the typing-module
from typing import List
c: List[str]
Type hints for functions#
We can use type hints for each input argument of a function by declaring the type after a colon. The return type is declared after the function brackets by using ->
followed by the type before starting the function block.
def triple(x: float) -> float:
print(f"Computing 3 * {x}")
return 3.0 * x
We can use typing.Callable
to give type definititons for a function.
from typing import Callable
def half(x: float) -> float:
print(f"Computing 0.5 * {x}")
return 0.5 * x
def f_of_g(f: Callable[[float], float], g: Callable[[float], float], x: float) -> float:
return f(g(x))
print(f"Computed: {f_of_g(triple, half, 10)}")
Computing 0.5 * 10
Computing 3 * 5.0
Computed: 15.0
Union types#
Some times a function can take in an argument that can have multiple types, we use the typing.Union[type_x, type_y,...]
operator (for Python < 3.10) or the |
operator in (Python >= 3.10) for this
def user_id(name: str, year: int | str):
if isinstance(year, str):
return name + year
elif isinstance(year, int):
return name + str(year)
else:
raise ValueError(f"Unsupported input: {type(year)=}.")
user_id("James", 1705)
'James1705'
user_id("James", "1992")
'James1992'
Other types of importance#
typing.Optional
- Used when an input argument to a function is optional (i.e. default value in function isNone
). Equivalent toUnion[type_x, None]
.typing.Dict[x, y]
. In Python >= 3.9 one can usedict[x, y]
to decleare that all keys of a dictionary is of typex
, while all values are of typey
.typing.Protocol
- Static duck typing, PEP544. Used to make sure that type-checkers can work with the duck-typing priciple.
Resources#
Python Typing module for more information.