Functions_Comprehensions_and

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 single tuple that the function body can work with.
    • The conventional name is args, but the asterisk is what activates the behavior. You could write *numbers or *values and the mechanism works identically — the name args is simply a widely adopted convention.
    • *args allows a function to accept any number of positional arguments, including zero. If no extra positional arguments are supplied by the caller, args will simply be an empty tuple rather than causing an error.
    • Inside the function, args can be iterated, indexed, or passed to other functions. Because it is a regular tuple, you can loop over it with for, access elements by index, check its length, or unpack it into another call.
# *args example
def total(*args):
    return sum(args)

print(total(1, 2, 3))   # 6
print(total())          # 0
  • 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 a dict keyed by argument name.
    • The conventional name is kwargs, but only the double asterisk is syntactically required. Naming the parameter **options or **config is perfectly valid; the double-star syntax alone determines the collection behavior.
    • **kwargs allows 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 on kwargs inside the function body.
# **kwargs example
def greet(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

greet(name="Alice", language="Python")
  • Combining *args and **kwargs
    • Regular parameters, *args, and **kwargs can 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 a SyntaxError, so understanding the hierarchy is critical.
    • This pattern is heavily used in decorator and middleware design. A wrapper function that accepts *args, **kwargs and 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 args or kwargs as appropriate.
# Combined signature
def mixed(required, *args, flag=False, **kwargs):
    print(required, args, flag, kwargs)

mixed("hello", 1, 2, flag=True, extra="yes")
  • Unpacking Arguments at the Call Site
    • Using *iterable in a call unpacks the iterable into individual positional arguments. For example, func(*my_list) is equivalent to passing each element of my_list as a separate positional argument, saving you from writing them out explicitly.
    • Using **mapping in a call unpacks the dictionary into individual keyword arguments. Each key becomes a parameter name and its associated value becomes the argument, so func(**config) is equivalent to spelling out every key=value pair 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, **kwargs unpacking to pass through arguments without hard-coding any specific signature.
# 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.