For variable positional arguments (*args) and variable keyword arguments (**kw) you only need to specify the expected value for one such argument.

From the Arbitrary argument lists and default argument values section of the Type Hints PEP:

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

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

is acceptable and it means that, e.g., all of the following represent function calls with valid types of arguments:

foo('a', 'b', 'c')
foo(x=1, y=2)
foo('', z=0)

So you'd want to specify your method like this:

def foo(*args: int):

However, if your function can only accept either one or two integer values, you should not use *args at all, use one explicit positional argument and a second keyword argument:

def foo(first: int, second: Optional[int] = None):

Now your function is actually limited to one or two arguments, and both must be integers if specified. *args always means 0 or more, and can't be limited by type hints to a more specific range.

Answer from Martijn Pieters on Stack Overflow
Top answer
1 of 9
570

For variable positional arguments (*args) and variable keyword arguments (**kw) you only need to specify the expected value for one such argument.

From the Arbitrary argument lists and default argument values section of the Type Hints PEP:

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

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

is acceptable and it means that, e.g., all of the following represent function calls with valid types of arguments:

foo('a', 'b', 'c')
foo(x=1, y=2)
foo('', z=0)

So you'd want to specify your method like this:

def foo(*args: int):

However, if your function can only accept either one or two integer values, you should not use *args at all, use one explicit positional argument and a second keyword argument:

def foo(first: int, second: Optional[int] = None):

Now your function is actually limited to one or two arguments, and both must be integers if specified. *args always means 0 or more, and can't be limited by type hints to a more specific range.

2 of 9
143

2022 Update

The mypy team added support for Unpack, this is available since Mypy 0.981 or higher.

Attention! Although this feature is complete, Unpack[...] is still considered experimental, so you will need to use --enable-incomplete-features to enable it.

You can use this feature as follows:

from typing import TypedDict
from typing_extensions import Unpack


class RequestParams(TypedDict):
    url: str
    allow_redirects: bool


def request(**kwargs: Unpack[RequestParams]) -> None:
    ...

If you call the request function with the arguments defined in the TypedDict, you won't get any errors:

# OK
request(url="https://example.com", allow_redirects=True)

If you forget to pass an argument, mypy will let you know now

# error: Missing named argument "allow_redirects" for "request"  [call-arg]
request(url="https://example.com")

You can also make the fields non-required by adding total=False to the TypedDict:

class RequestParams(TypedDict, total=False):
    url: str
    allow_redirects: bool

# OK
request(url="https://example.com")

Alternatively, you can use the Required and NotRequired annotations to control whether a keyword argument is required or not:

from typing import TypedDict
from typing_extensions import Unpack, NotRequired


class RequestParams(TypedDict):
    url: str
    allow_redirects: NotRequired[bool]

def request(**kwargs: Unpack[RequestParams]) -> None:
    ...

# OK
request(url="https://example.com", allow_redirects=True)

Old answer below:

While you can annotate variadic arguments with a type, I don't find it very useful because it assumes that all arguments are of the same type.

The proper type annotation of *args and **kwargs that allows specifying each variadic argument separately is not supported by mypy yet. There is a proposal for adding an Expand helper on mypy_extensions module, it would work like this:

class Options(TypedDict):
    timeout: int
    alternative: str
    on_error: Callable[[int], None]
    on_timeout: Callable[[], None]
    ...

def fun(x: int, *, **options: Expand[Options]) -> None:
    ...

The GitHub issue was opened on January 2018 but it's still not closed. Note that while the issue is about **kwargs, the Expand syntax will likely be used for *args as well.

🌐
Python
peps.python.org › pep-0692
PEP 692 – Using TypedDict for more precise **kwargs typing | peps.python.org
May 29, 2022 - Currently **kwargs can be type hinted as long as all of the keyword arguments specified by them are of the same type. However, that behaviour can be very limiting. Therefore, in this PEP we propose a new way to enable more precise **kwargs typing.
Discussions

Python Type Hints – *args and **kwargs (2021)
If your function just wraps another you can use the same type hints as the other function with functools.wraps https://docs.python.org/3/library/functools.html#functools.w · Underneath, this is because many (most?) type checkers for Python aren't actually running the code in order to access ... More on news.ycombinator.com
🌐 news.ycombinator.com
149
267
August 31, 2023
ParamSpec: use P.args and P.kwargs in other scopes, such as return types
I have a number of cases where it would be very useful to use P.args and P.kwargs of a ParamSpec to annotate tuple and dict objects, for example, when extracting arguments to pass to a function in ... More on github.com
🌐 github.com
14
September 13, 2022
Variadic typing for kwargs, namedtuple - Typing - Discussions on Python.org
I have an API pattern where the caller of a function can name inputs on-the-fly (via kwargs) and those same names are accessible in the return object (typically namedtuple or dynamically created NamedTuple). For example, a short-circuiting, async wait_any() intended for a heterogenous set of ... More on discuss.python.org
🌐 discuss.python.org
1
November 21, 2023
Is it possible to type hint `tuple` and `dict` equivalent to `P.args` and `P.kwargs`?
Is it possible to "unpack" ParamSpec args and kwargs into a tuple/dict? Context: let's annotate a function that takes a Callable, it's args and kwargs and evaluates the callable. ... More on github.com
🌐 github.com
1
1
August 18, 2023
🌐
GitHub
github.com › python › typing › discussions › 1501
Annotating a function with passthrough kwargs · python/typing · Discussion #1501
If you really want to avoid any potential breaking changes, you'd need to support both *args and **kwargs in your run method because third_party_func could theoretically be modified in the future to accept positional arguments. The reason you're not seeing a return type in mypy is that it never infers return types for functions.
Author   python
🌐
Python documentation
docs.python.org › 3 › library › typing.html
typing — Support for type hints
1 month ago - Changed in version 3.12: Parameter specifications can now be declared using the type parameter syntax introduced by PEP 695. Changed in version 3.13: Support for default values was added. ... Only parameter specification variables defined in global scope can be pickled. ... Arguments and keyword arguments attributes of a ParamSpec. The P.args attribute of a ParamSpec is an instance of ParamSpecArgs, and P.kwargs is an instance of ParamSpecKwargs.
🌐
Rednafi
rednafi.com › python › annotating args and kwargs in python
Annotating args and kwargs in Python | redowan's reflections
January 8, 2024 - from typing import TypedDict, Unpack # Python 3.12+ # from typing_extensions import TypedDict, Unpack # < Python 3.12 class Kw(TypedDict): key1: int key2: bool def foo(*args: Unpack[tuple[int, str]], **kwargs: Unpack[Kw]) -> None: ...
🌐
Lobsters
lobste.rs › s › abigdb › annotating_args_kwargs_python
Annotating args and kwargs in Python | Lobsters
January 10, 2024 - The only difference is whether you spell the check if foo is None or if "foo" in kwargs; the second option does not get you out of doing the check in the function body. Similarly, if what you want is to take an unbounded sequence of arguments of a particular type, just take... a sequence: def my_function(foo: list[int | float]) -> SomeReturnType: ... instead of trying to type-hint an *args declaration. tl;dr Python already has a way to precisely express optional arguments and sequences-as-arguments.
Find elsewhere
🌐
W3Schools
w3schools.com › python › python_args_kwargs.asp
Python *args and **kwargs
Arbitrary Keyword Arguments are often shortened to **kwargs in Python documentation. The **kwargs parameter allows a function to accept any number of keyword arguments. Inside the function, kwargs becomes a dictionary containing all the keyword ...
🌐
Hacker News
news.ycombinator.com › item
Python Type Hints – *args and **kwargs (2021) | Hacker News
August 31, 2023 - If your function just wraps another you can use the same type hints as the other function with functools.wraps https://docs.python.org/3/library/functools.html#functools.w · Underneath, this is because many (most?) type checkers for Python aren't actually running the code in order to access ...
🌐
GitHub
github.com › python › typing › issues › 1252
ParamSpec: use P.args and P.kwargs in other scopes, such as return types · Issue #1252 · python/typing
September 13, 2022 - I have a number of cases where it would be very useful to use P.args and P.kwargs of a ParamSpec to annotate tuple and dict objects, for example, when extracting arguments to pass to a function in ...
Author   chadrik
🌐
Python.org
discuss.python.org › typing
Variadic typing for kwargs, namedtuple - Typing - Discussions on Python.org
November 21, 2023 - I have an API pattern where the caller of a function can name inputs on-the-fly (via kwargs) and those same names are accessible in the return object (typically namedtuple or dynamically created NamedTuple). For example, a short-circuiting, async wait_any() intended for a heterogenous set of input functions, where return values of interest can be captured: results = await wait_any(foo, bar_finished=bar, baz_status=baz) if results.bar_finished: ... In my organization, the utility of this patte...
🌐
how.wtf
how.wtf › using-type-annotations-with-kwargs-in-python.html
Using type annotations with kwargs in Python | how.wtf
January 26, 2024 - Python 3.12 released a new method for explicitly typing kwargs: using a TypedDict + Unpack.
🌐
Python
docs.python.org › fr › 3.10 › library › typing.html
typing — Prise en charge des annotations de type — Documentation Python 3.10.19
October 10, 2025 - typing.ParamSpecKwargs¶ · Arguments and keyword arguments attributes of a ParamSpec. The P.args attribute of a ParamSpec is an instance of ParamSpecArgs, and P.kwargs is an instance of ParamSpecKwargs. They are intended for runtime introspection and have no special meaning to static type checkers.
🌐
Real Python
realpython.com › python312-typing
Python 3.12 Preview: Static Typing Improvements – Real Python
October 21, 2023 - You can now use TypedDict, or typed dictionary, to add more precise type hints to **kwargs. This is specified in PEP 692. A TypedDict is used together with a static type checker to enforce the names of dictionary keys and the types of dictionary values. You can, for example, define the following: ... >>> from typing import TypedDict >>> class Options(TypedDict): ... line_width: int ... level: str ... propagate: bool ... ... Up until now, Python has used these typed dictionaries to give more information about regular dictionaries.
🌐
podhmo's diary
pod.hatenablog.com › entry › 2021 › 04 › 11 › 120850
pythonで**kwargsにもう少し細かく型を付けたい - podhmo's diary
April 11, 2021 - 例えば以下の様な関数helloがあるとする。可変長引数を使って定義されている。とてもtrivialな例ではあるけれど説明用なので。。 from typing import Any def greet(prefix: str, *, name: str) -> None: print(f"{prefix}, {name}") def hello(**params: Any) -> None: greet("hello", **params) ここでhelloを呼び出す際に以下のようにtypoしたとする。これをmypyなどの静的解析で検知したい。 # TypeError: greet() got …
🌐
Gihyo
gihyo.jp › article › 2023 › 09 › monthly-python-2309
Python 3.12の新機能「PEP 692: Using TypedDict for more precise **kwargs typing」の紹介 | gihyo.jp
September 8, 2023 - from typing import TypedDict, Unpack class Example(TypedDict): foo: str bar: int def example(**kwargs: Unpack[Example]) -> None: ...
🌐
CSDN
devpress.csdn.net › python › 62fe04b47e66823466192f83.html
Type annotation for Callable that takes **kwargs_python_Mangs-Python
August 18, 2022 - It allows you to define a Protocol subclass that describes the behaviors of the type, pretty much like an "interface" or "trait" in other languages. For functions, a similar syntax is supported: from typing import Protocol # from typing_extensions import Protocol # if you're using Python 3.6 class MyFunction(Protocol): def __call__(self, a: Any, b: Any, **kwargs) -> bool: ...
🌐
SQL Pac
sqlpac.com › articles › conception → python › python - understanding args, kwargs in functions
Python - Understanding args, kwargs in functions
Let’s explain *args and **kwargs through basic examples. ... This function works well if there is only 2 integer arguments. *args is used to specify several arguments : ... The unpacking operator * is important here. args in the function is then a tuple object, not a list object. Tuple objects are immutable, values cannot be changed after assignment. def sum(*args): print(args) print(type(args)) r = 0 for x in args: r += x return r print(sum(10,20,30,40)) (10, 20, 30, 40) <class 'tuple'> 100
🌐
The Python Coding Book
thepythoncodingbook.com › home › blog › using type hints when defining a python function [intermediate python functions series #6]
Using type hints when defining a Python function
March 19, 2023 - Type hints indicate that the data the function returns is a list of strings. Therefore the IDE “knows” that item should be a str in the final for loop since result is a list of strings. The IDE warns you that append() is not a string method. Opinions are split in the Python community on how and when you should use type hinting.