how.wtf

Using type annotations with kwargs in Python

· Thomas Taylor

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:

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.

#python  

Reply to this post by email ↪