https://adamj.eu/tech/2021/05/11/python-type-hints-args-and-kwargs/ Adam Johnson Home | Blog | Books | Projects | Colophon | Contact Python Type Hints - *args and **kwargs 2021-05-11A 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 example.py example.py:5: error: Argument 1 to "variable" has incompatible type "int"; expected "Tuple[int, ...]" example.py:5: error: Argument 2 to "variable" has incompatible type "int"; expected "Tuple[int, ...]" example.py:5: error: Argument 3 to "variable" has incompatible type "int"; expected "Tuple[int, ...]" example.py:5: error: Argument "a" to "variable" has incompatible type "int"; expected "Dict[str, int]" example.py:5: error: Argument "b" to "variable" has incompatible type "int"; expected "Dict[str, int]" example.py:5: 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 example.py Success: no issues found in 1 source file Yay! Fin I'd like to have an argument, please. --Adam --------------------------------------------------------------------- Learn how to make your tests run quickly in my book Speed Up Your Django Tests. --------------------------------------------------------------------- Subscribe via RSS, Twitter, Mastodon, or email: Your email address:[ ] [ ] [Subscribe] One summary email a week, no spam, I pinky promise. Related posts: * Python Type Hints - Use object instead of Any * Python Type Hints - How to Specify a Class Rather Than an Instance Thereof * Python Type Hints - How to Fix Circular Imports Tags: mypy, python(c) 2021 All rights reserved. Code samples are public domain unless otherwise noted.