Skip to content

Python Type Annotations: How to Annotate Variable Arguments

Learn how to properly annotate *args and **kwargs in Python type hints by specifying individual argument types rather than container types.

In Python, the special syntax *args and **kwargs allows functions to accept a variable number of arguments. However, when using type hints, beginners often misinterpret how to annotate them.

The key rule is: You annotate the type of the individual element, not the container (tuple or dict) that holds them.

Annotating *args (Positional Arguments)

When you define *args, Python internally collects all extra positional arguments into a tuple. However, the type hint should specify the type of each item inside that tuple, not the tuple itself.

Incorrect Way

def sum_numbers(*args: tuple[int, ...]) -> int:
    return sum(args)

Correct Way

def sum_numbers(*args: int) -> int:
    return sum(args)

sum_numbers(1, 2, 3) # Valid

Annotating **kwargs (Keyword Arguments)

When you define **kwargs, Python collects keyword arguments into a dict where keys are always strings. The type hint should specify the type of the values associated with those keys.

Incorrect Way

def print_config(**kwargs: dict[str, str]):
    pass

Correct Way

def print_config(**kwargs: str) -> None:
    for k, v in kwargs.items():
        print(f"{k}: {v}")

print_config(host="localhost", port="8080") 

Combining Annotations

Here is a complete example showing both variable positional and keyword arguments annotated correctly.

from typing import Any

def logger(message: str, *args: Any, **kwargs: Any) -> None:
    """
    Logs a message with optional extra details.
    """
    print(f"MSG: {message}")
    
    if args:
        print(f"Extra data: {args}")
    
    if kwargs:
        print(f"Context: {kwargs}")

logger("System Start", 1, [2, 3], user="admin", id=101)

Conclusion

To annotate variable arguments correctly:

  1. *args: Annotate the type of the items (e.g., *args: int), not the tuple.
  2. **kwargs: Annotate the type of the values (e.g., **kwargs: str), not the dictionary. Keys are implicitly strings.
Navigation