Friday, March 1, 2024
Google search engine
HomeUncategorizedPython Type Hints – *args and **kwargs

Python Type Hints – *args and **kwargs

A typographer’s governor (apparently).

When I started writing type hints, I was a little confused about what to do with Python’s variable argument operators, * and ** (often called *args and **kwargs). Here’s what I figured out.

Recall that the * operator captures variable positional arguments in a tuple, and ** captures variable keyword arguments in a dict. For example, take this function:

def variable(*args, **kwargs):

In the function body, args will be a tuple, and kwargs a dict with string keys.

When adding type hints, it seems natural to try declare the full types of args and kwargs. If we wanted all our values to be ints, we might try:

def variable(*args: tuple[int, ...], **kwargs: dict[str, int]) -> None:

(The ... in the tuple definition makes it a tuple of any length.)

But this is incorrect. We can check by adding a call:

variable(1, 2, 3, a=4, b=5, c=6)

Running Mypy on the file, it finds a problem with every argument(!):

$ mypy error: Argument 1 to "variable" has incompatible type "int"; expected "Tuple[int, ...]" error: Argument 2 to "variable" has incompatible type "int"; expected "Tuple[int, ...]" error: Argument 3 to "variable" has incompatible type "int"; expected "Tuple[int, ...]" error: Argument "a" to "variable" has incompatible type "int"; expected "Dict[str, int]" error: Argument "b" to "variable" has incompatible type "int"; expected "Dict[str, int]" error: Argument "c" to "variable" has incompatible type "int"; expected "Dict[str, int]"
Found 6 errors in 1 file (checked 1 source file)

Uh oh! What’s the right way then?

* always binds to a tuple, and ** always binds to a dict with string keys. Because of this restriction, type hints only need you to define the types of the contained arguments. The type checker automatically adds the tuple[_, ...] and dict[str, _] container types.

The Python Enhancement Proposal (PEP) that introduced type hints, PEP 484, specified this rule:

Arbitrary argument lists can as well be type annotated, so that the definition:

def foo(*args: str, **kwds: int): ...

is acceptable… In the body of function foo, the type of variable args is deduced as Tuple[str, ...] and the type of variable kwds is Dict[str, int].

So, we can correctly type our function as:

def variable(*args: int, **kwargs: int) -> None:

This then passes type checks:

$ mypy
Success: no issues found in 1 source file



I’d like to have an argument, please.


Learn how to make your tests run quickly in my book Speed Up Your Django Tests.

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: mypy, python

Read More



Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments