Sometimes you don't know in advance how many arguments a function will be called with. Python's *args and **kwargs let a function accept any number of positional arguments and any number of keyword arguments, without you having to list every possible parameter name up front. You'll see this pattern constantly in library code — logging helpers, wrapper/decorator functions, and anything designed to be flexible.

Using *args for Extra Positional Arguments

def total(*args):
    # args is a tuple of every positional argument passed in
    print(f"Received: {args}")
    return sum(args)


print(total(1, 2, 3))
print(total(10, 20, 30, 40))

Output:

Received: (1, 2, 3)
6
Received: (10, 20, 30, 40)
100

Using **kwargs for Extra Keyword Arguments

def build_profile(**kwargs):
    # kwargs is a dict of every keyword argument passed in
    for key, value in kwargs.items():
        print(f"{key}: {value}")


build_profile(name="Alice", age=28, city="New York")

Output:

name: Alice
age: 28
city: New York

Combining Regular Arguments, *args, and **kwargs

def describe_event(event_name, *attendees, **details):
    print(f"Event: {event_name}")
    print(f"Attendees: {list(attendees)}")
    print(f"Details: {details}")


describe_event("Team Meeting", "Alice", "Bob", "Charlie", room="204", duration="1h")

Output:

Event: Team Meeting
Attendees: ['Alice', 'Bob', 'Charlie']
Details: {'room': '204', 'duration': '1h'}

The names args and kwargs are just convention — what actually matters is the * and **. A single * collects any extra positional arguments into a tuple; a double ** collects any extra keyword arguments into a dictionary. Python also requires a strict order when combining them: regular named parameters first, then *args, then **kwargs — reversing that order is a syntax error.

This same */** syntax also works in reverse, to unpack a list or dictionary into a function call — e.g. total(*[1, 2, 3]) or build_profile(**{"name": "Bob"}) — which is exactly how a lot of "pass-through" wrapper functions and decorators forward arguments to the function they're wrapping.