Splat Operator
Splat Operator
Section titled βSplat OperatorβWhat it is
Section titled βWhat it isβThe splat operators * and ** let a function accept a variable number of positional or keyword arguments. *args collects extra positional arguments into a tuple; **kwargs collects extra keyword arguments into a dict. The same operators also unpack a sequence or mapping into a function call β that dual use is what confuses people first.
Before this feature
Section titled βBefore this featureβWithout splats, a function can only accept a fixed, known set of parameters. To support βany number of arguments,β older code had to accept a single list or dict and require callers to build it explicitly:
def total(numbers_list): return sum(numbers_list)
total([1, 2, 3, 4]) # caller must remember to wrap in a listAfter this feature
Section titled βAfter this featureβdef total(*args): return sum(args)
total(1, 2, 3, 4) # args == (1, 2, 3, 4)
def build_profile(**kwargs): return kwargs
build_profile(name="Alice", age=30) # kwargs == {'name': 'Alice', 'age': 30}
# Combined, in the required order: positional, *args, keyword-only, **kwargsdef request(url, *args, timeout=10, **kwargs): print(url, args, timeout, kwargs)
request("/api", "extra", retries=3)# /api ('extra',) 10 {'retries': 3}Splats also unpack on the call side:
def add(a, b, c): return a + b + c
nums = [1, 2, 3]add(*nums) # unpacks list into positional args -> 6
opts = {"a": 1, "b": 2, "c": 3}add(**opts) # unpacks dict into keyword args -> 6Why this is better
Section titled βWhy this is betterβ- Flexible APIs: Functions like
print(),max(), anddict.update()accept any number of arguments because theyβre built on*args/**kwargs. - Wrapper functions: Decorators forward arbitrary arguments to the wrapped function without knowing its signature (
def wrapper(*args, **kwargs): return func(*args, **kwargs)). - No dummy containers: Callers pass arguments naturally instead of manually building a list/dict just to satisfy the function signature.
Key notes / edge cases
Section titled βKey notes / edge casesβ*argsis a tuple,**kwargsis a dict β inside the function, not at the call site.- The names
args/kwargsare convention only;*values, **optionsworks identically. - Order in a
defmatters: positional params β*argsβ keyword-only params β**kwargs. - You canβt have two
*args-style catches in one signature, but you can mix explicit keyword-only args after*args. - Unpacking too few/many items into fixed positional parameters (
a, b = *[1,2,3]-style mistakes) raisesTypeError, not a silent truncation.
def f(a, *, b, **kwargs): # b is keyword-only after the bare * print(a, b, kwargs)
f(1, b=2, c=3) # 1 2 {'c': 3}f(1, 2) # TypeError: f() takes 1 positional argument but 2 were givenQuick practice
Section titled βQuick practiceβ-
What type is
argsinsidedef f(*args): ...when called asf(1, 2, 3)?Answer
A tuple: `(1, 2, 3)`. -
Given
def greet(**kwargs): ..., how do you call it by unpackingd = {"name": "Sam"}?Answer
`greet(**d)` β the double-star unpacks the dict into keyword arguments. -
Why must
**kwargsalways come last in a function signature?Answer
Because everything after a bare positional/`*args` collection point must be identifiable by keyword, and `**kwargs` is the catch-all for *any* remaining keyword β nothing can meaningfully follow it.