Using type annotations with kwargs in Python
Historically in Python, adding explicit typing for kwargs
was not directly supported; however, Python 3.12 added that ability.
In this post, we’ll go over methods for adding types to the kwargs
argument.
Adding type annotations to kwargs
Python 3.12 released a new method for explicitly typing kwargs
: using a TypedDict
+ Unpack
.
1from typing import NotRequired, TypedDict, Unpack
2
3
4class Parameters(TypedDict):
5 foo: int
6 bar: str
7 baz: NotRequired[str]
8
9
10def some_function(**kwargs: Unpack[Parameters]) -> None:
11 ...
12
13
14some_function(foo=1, bar="qux")
Here’s what happened:
Parameters
inherits fromTypedDict
NotRequired
represents an optional argumentkwargs
is typed using theUnpack[Parameters]
type
mypy
respects the typing.
Adding type annotations before Python 3.12
Before Python 3.12, there was not a clear way to explicitly declare typing for kwargs
. The closest solutions are covered below.
Using TypedDict
Instead of using kwargs
, ditch it for a TypedDict. For example:
1from typing import TypedDict
2
3
4class Parameters(TypedDict):
5 foo: int
6 bar: str
7 baz: str | None
8
9
10def some_function(params: Parameters) -> None:
11 ...
12
13
14some_function(Parameters(foo=1, bar="test", baz=None))
It’s not great, but it provides structured typing and feedback using mypy
.
Using Any
Simply using Any
will suffice with mypy
as well:
1from typing import Any
2
3
4def some_function(**kwargs: Any) -> None:
5 ...
6
7
8some_function(foo="bar")
But this is obviously not type safe since it disables type checking.
Using a single type
If your use-case allows it, you may define one type for the entire kwargs
argument:
1def some_function(**kwargs: str) -> None:
2 ...
3
4
5some_function(foo="bar", bar="qux")
Unfortunately, this locks you into having the same type supplied per each argument.