Functions
*args and **kwargs
Variable-length positional and keyword arguments
Interview: Very common in interviews — essential for decorators, wrappers, and flexible APIs
args and *kwargs allow functions to accept a variable number of arguments. args collects extra positional arguments into a tuple, while *kwargs collects extra keyword arguments into a dictionary. These are fundamental for writing flexible APIs, decorators, and wrapper functions.
args (Variable Positional)
- Collects extras: Any positional arguments beyond the defined parameters go into args as a tuple
- Can be empty: If no extra positional args are passed, args is an empty tuple ()
- Name convention: The name "args" is convention — technically
*anythingworks, but*argsis standard - Unpacking: The operator also unpacks iterables when calling —
func(*[1, 2, 3])
kwargs (Variable Keyword)
- Collects extras: Any keyword arguments not in the defined parameters go into kwargs as a dict
- Can be empty: If no extra keyword args are passed, kwargs is an empty dict {}
- Unpacking:
also unpacks dicts when calling —func(**{"a": 1}) - Order preserved: kwargs maintains insertion order since Python 3.7
The Decorator Pattern
The most important use of args/*kwargs is in decorator wrappers: def wrapper(*args, **kwargs): return func(*args, **kwargs). This forwards ALL arguments to the wrapped function without knowing its signature — essential for generic decorators.
Combining All Parameter Types
The full parameter signature follows this order:
def func(required, optional=default, *args, keyword_only, **kwargs)- Required args must be provided first
argscaptures remaining positional args- Keyword-only args (after
) must use keyword syntax **kwargscaptures any remaining keyword args
Common Pitfall: in Different Contexts
The operator has different meanings: in function definition, it collects arguments; in function call, it unpacks iterables. Similarly, ** in definition collects kwargs, but in call unpacks dicts. Don't confuse these two uses!
Use Cases
Writing generic decorator wrappers that work with any function signature
Building flexible configuration functions with default + override merging
Creating wrapper/proxy functions that forward all arguments to another function
Designing APIs that accept variable numbers of items (batch operations)
Implementing printf-style debugging with automatic argument logging
Common Mistakes
Forgetting to forward *args, **kwargs in wrapper functions — breaks the wrapped function's signature
Confusing * in definition (collects) vs * in call (unpacks) — they do opposite things
Using mutable default values in combination with **kwargs — dict.update() modifies in-place
Not using functools.wraps in decorators — loses the original function's name and docstring
Accepting **kwargs without documenting what keys are expected — makes the API opaque