Python*args & **kwargs

*args & **kwargs

Sometimes you don't know in advance how many arguments a function will need to accept. Python solves this with two special parameters: *args for a variable number of positional arguments, and **kwargs for a variable number of keyword arguments. The names args and kwargs are just convention — the * and ** are what actually matter.

`*args`: Variable Positional Arguments

A parameter prefixed with a single * collects any extra positional arguments into a tuple.

Python
def total(*args):
    print(f"Received: {args}")
    return sum(args)


print(total(1, 2, 3))
print(total(10, 20, 30, 40))
print(total())
Received: (1, 2, 3)
6
Received: (10, 20, 30, 40)
100
Received: ()
0
`**kwargs`: Variable Keyword Arguments

A parameter prefixed with ** collects any extra keyword arguments into a dictionary, where the keys are the argument names as strings.

Python
def print_profile(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")


print_profile(name="Ada", role="Engineer", years=12)
name: Ada
role: Engineer
years: 12
Combining Regular Parameters with `*args`/`**kwargs`

Regular parameters, *args, and **kwargs can all appear in the same signature. Python requires a strict order: normal parameters first, then *args, then keyword-only parameters, then **kwargs last.

Python
def build_report(title, *sections, author="Anonymous", **metadata):
    print(f"Report: {title} (by {author})")
    print("Sections:", sections)
    print("Metadata:", metadata)


build_report(
    "Q3 Sales",
    "Summary",
    "Charts",
    "Appendix",
    author="Ada",
    version=2,
    confidential=True,
)
Report: Q3 Sales (by Ada)
Sections: ('Summary', 'Charts', 'Appendix')
Metadata: {'version': 2, 'confidential': True}
Unpacking When Calling a Function

The * and ** symbols also work in reverse — at the call site, they unpack an existing list/tuple or dictionary into separate positional or keyword arguments.

Python
def add(a, b, c):
    return a + b + c


numbers = [1, 2, 3]
print(add(*numbers))  # unpacks to add(1, 2, 3)

data = {"a": 10, "b": 20, "c": 30}
print(add(**data))    # unpacks to add(a=10, b=20, c=30)
6
60

This is extremely handy for forwarding arguments from one function to another without knowing their exact shape.

Python
def call_and_log(func, *args, **kwargs):
    print(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
    result = func(*args, **kwargs)
    print(f"Result: {result}")
    return result


def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"


call_and_log(greet, "Ada", greeting="Hi")
Calling greet with args=('Ada',), kwargs={'greeting': 'Hi'}
Result: Hi, Ada!
Tip
This "catch everything and forward it" pattern is exactly how many real-world decorators, logging wrappers, and API client libraries stay flexible regardless of what function they're wrapping.
Real-World Example: A Flexible Logging Wrapper

Here's a small utility that logs every call to any function, regardless of its signature, by capturing arguments with *args/**kwargs and forwarding them.

Python
import time


def timed_call(func, *args, **kwargs):
    """Run func with the given arguments and log how long it took."""
    start = time.perf_counter()
    result = func(*args, **kwargs)
    elapsed = time.perf_counter() - start
    print(f"{func.__name__}(...) took {elapsed:.6f}s -> {result!r}")
    return result


def power(base, exponent=2):
    return base ** exponent


timed_call(power, 2, exponent=16)
timed_call(sum, [1, 2, 3, 4, 5])
power(...) took 0.000002s -> 65536
sum(...) took 0.000001s -> 15
Warning
`*args` and `**kwargs` make a function very flexible, but overusing them can hide what a function actually expects. Prefer explicit named parameters when the set of arguments is known and fixed; reserve `*args`/`**kwargs` for genuinely variadic cases or generic wrappers (like decorators).
Quick Reference

Syntax

Where used

Collects into

*args (definition)

function signature

A tuple of extra positional args

**kwargs (definition)

function signature

A dict of extra keyword args

*iterable (call)

function call

Unpacks into positional arguments

**mapping (call)

function call

Unpacks into keyword arguments

  • The names args and kwargs are conventions — *values and **options work identically.

  • A function can accept both *args and **kwargs at once for maximum flexibility.

  • Unpacking with */** at a call site works with any function, not just ones defined with *args/**kwargs.