Skip to content

Parameter Unpacking: *args and **kwargs#

Python functions can accept flexible argument lists using *args (extra positional → tuple) and **kwargs (extra keyword → dict). Unpacking also works at call time with * and **.

How to use this page

Builds on Functions and Scope. Used heavily in decorators and wrappers.

At a glance
Track Python Advanced → Advanced Functions
Sections 7 major topics
Outline Use the right-hand TOC to jump

Topics: *args — variadic positional · **kwargs — variadic keyword · Keyword-only parameters (PEP 3102) · Positional-only parameters (PEP 570, 3.8+) · Unpacking at call site · Decorator wrapper pattern · Exhausting iterables into arguments

  1. *args — variadic positional
  2. **kwargs — variadic keyword
  3. Keyword-only parameters (PEP 3102)
  4. Positional-only parameters (PEP 570, 3.8+)
  5. Unpacking at call site
  6. Decorator wrapper pattern
  7. Exhausting iterables into arguments

*args — variadic positional#

def log(msg, *args):
    print(msg, args)

log("values:", 1, 2, 3)   # values: (1, 2, 3)

*args is a convention — name it anything, but * is required.

Ordering rules in definition#

def f(a, b, *args, c=0):
    ...

# a, b — required positional
# *args — captures rest
# c — keyword with default

**kwargs — variadic keyword#

def configure(**kwargs):
    for k, v in kwargs.items():
        print(f"{k}={v}")

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

Must come after *args (or after bare * for keyword-only section):

def f(a, *args, **kwargs):
    ...

Keyword-only parameters (PEP 3102)#

def connect(host, port, *, timeout=30, ssl=True):
    ...

connect("localhost", 8080, timeout=60)
# connect("localhost", 8080, 60)  # TypeError

Bare * forces following params to be keyword-only.


Positional-only parameters (PEP 570, 3.8+)#

def div(a, b, /):
    return a / b

div(10, 2)      # OK
# div(a=10, b=2)  # TypeError

/ separates positional-only from positional-or-keyword.


Unpacking at call site#

def f(a, b, c):
    return a + b + c

args = (1, 2, 3)
f(*args)   # 6

kwargs = {"a": 1, "b": 2, "c": 3}
f(**kwargs)

f(1, *[2, 3])
f(**{"a": 1}, **{"b": 2, "c": 3})

Merge dicts into kwargs#

defaults = {"timeout": 30, "retries": 3}
overrides = {"timeout": 60}
call(**{**defaults, **overrides})   # timeout=60

Decorator wrapper pattern#

from functools import wraps

def decorator(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        # forward everything to wrapped function
        return fn(*args, **kwargs)
    return wrapper

Universal forwarding — why decorators use *args, **kwargs.


Exhausting iterables into arguments#

def minmax(a, b, c):
    return min(a, b, c), max(a, b, c)

values = [3, 1, 4]
minmax(*values)   # (1, 4)

Requires exact count unless using *args.


Interview traps (quick reference)#

Trap What goes wrong Safe approach
**kwargs before *args SyntaxError *args then **kwargs
Wrong unpack count TypeError Match signature
Mutating kwargs dict Affects caller Copy if needed
Keyword-only forgotten TypeError on call Document API

Mental model checklist#

  1. What type is args inside def f(*args)?
  2. What does f(**d) require about keys?
  3. What does bare * in a signature mean?
  4. Why do decorators use *args, **kwargs?

What's next#

Topic Page
Decorators Decorators
Functions and scope Functions and Scope
Callable objects Callable Objects