1
Variable-Length Arguments: *args and **kwargs
▶Python provides two special syntactic mechanisms — *args and **kwargs — that let functions accept a variable number of arguments. These tools are essential for writing flexible, reusable APIs and for building patterns like decorators and middleware that must forward arguments without knowing them in advance.
- Collecting Positional Arguments with *args
- The
*prefix collects remaining positional arguments into an immutable tuple. Any positional values passed beyond the explicitly named parameters are gathered together into a singletuplethat the function body can work with. - The conventional name is
args, but the asterisk is what activates the behavior. You could write*numbersor*valuesand the mechanism works identically — the nameargsis simply a widely adopted convention. *argsallows a function to accept any number of positional arguments, including zero. If no extra positional arguments are supplied by the caller,argswill simply be an empty tuple rather than causing an error.- Inside the function,
argscan be iterated, indexed, or passed to other functions. Because it is a regular tuple, you can loop over it withfor, access elements by index, check its length, or unpack it into another call. - Collecting Keyword Arguments with **kwargs
- The
**prefix collects remaining keyword arguments into a regular dictionary. Any named arguments passed by the caller that do not match an explicitly declared parameter are bundled into adictkeyed by argument name. - The conventional name is
kwargs, but only the double asterisk is syntactically required. Naming the parameter**optionsor**configis perfectly valid; the double-star syntax alone determines the collection behavior. **kwargsallows callers to pass any named arguments not explicitly declared. This is especially useful when designing functions that need to remain open to future options without breaking existing call sites.- The resulting dictionary can be iterated with
.items()to process each key-value pair. Standard dictionary operations — including.get(), membership testing, and comprehension — are all available onkwargsinside the function body. - Combining *args and **kwargs
- Regular parameters,
*args, and**kwargscan all appear in a single function signature. This gives a function the ability to handle a fixed interface for required data while remaining open to arbitrary additional inputs. - The required declaration order is: positional,
*args, keyword-only,**kwargs. Python enforces this order at definition time; violating it raises aSyntaxError, so understanding the hierarchy is critical. - This pattern is heavily used in decorator and middleware design. A wrapper function that accepts
*args, **kwargsand forwards them to the wrapped callable can handle any underlying function signature transparently. - Callers can still supply regular positional and keyword arguments alongside variadic ones. Python assigns values to named parameters first, then routes any leftovers into
argsorkwargsas appropriate. - Unpacking Arguments at the Call Site
- Using
*iterablein a call unpacks the iterable into individual positional arguments. For example,func(*my_list)is equivalent to passing each element ofmy_listas a separate positional argument, saving you from writing them out explicitly. - Using
**mappingin a call unpacks the dictionary into individual keyword arguments. Each key becomes a parameter name and its associated value becomes the argument, sofunc(**config)is equivalent to spelling out everykey=valuepair by hand. - Both operators can be combined in a single call for maximum flexibility. A call like
func(*positional_data, **option_dict)merges both forms simultaneously, and Python resolves the full argument list before invoking the function. - This technique is commonly used to forward arguments from one function to another. Decorator wrappers, proxy objects, and adapter layers all rely on
*args, **kwargsunpacking to pass through arguments without hard-coding any specific signature.
# *args example
def total(*args):
return sum(args)
print(total(1, 2, 3)) # 6
print(total()) # 0
# **kwargs example
def greet(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
greet(name="Alice", language="Python")
# Combined signature
def mixed(required, *args, flag=False, **kwargs):
print(required, args, flag, kwargs)
mixed("hello", 1, 2, flag=True, extra="yes")
# Unpacking at the call site
values = [10, 20, 30]
options = {"sep": ", ", "end": "!\n"}
print(*values, **options)
NotesCovers the special *args and **kwargs syntax that allows functions to accept an arbitrary number of positional or keyword arguments. Explains how these mechanisms support flexible and reusable API design. Slides: 1, 2, 3, 4.