Quick Answer

In a function definition, *args collects extra positional arguments into a tuple and **kwargs collects extra keyword arguments into a dict. At a call site the same symbols do the reverse — they unpack a sequence or dict into arguments.

In a definition, they collect

def demo(a, *args, key=None, **kwargs):
    return a, args, key, kwargs

print(demo(1, 2, 3, key="k", x=9, y=10))
# (1, (2, 3), 'k', {'x': 9, 'y': 10})

Read the output against the call. a took the first positional argument. *args swept up the remaining positional ones into a tuple. key matched by name. **kwargs collected every other keyword argument into a dict.

The names are convention, not syntax — *items and **options work identically. The stars are what matter. Stick to args and kwargs anyway, because everyone reading your code expects them.

A practical use: a function accepting any number of values.

def total(*nums):
    return sum(nums)

print(total(1, 2, 3))   # 6

At a call site, they spread

This is the half people miss, and it is the more useful one day to day.

vals = [1, 2, 3]
print(total(*vals))     # 6 -- same as total(1, 2, 3)

d = {"a": 1, "b": 2}
def show(a, b): return a + b
print(show(**d))        # 3 -- same as show(a=1, b=2)

* unpacks a sequence into positional arguments; ** unpacks a dict into keyword arguments, matching keys to parameter names.

Without unpacking, total(vals) passes the list itself as a single argument, and sum then receives a tuple containing a list. That mismatch produces a confusing TypeError, and it is the usual reason someone reaches for the star operator for the first time.

Unpacking also works in literals: [*a, *b] merges lists and {**d1, **d2} merges dicts, with later keys winning.

The order Python requires

Parameters must be declared in this order, and getting it wrong is a syntax error rather than a runtime surprise:

def f(positional, default=1, *args, kw_only, **kwargs): ...

The interesting part is kw_only. Any parameter declared after *args can only be passed by name — there is no way to reach it positionally, because *args has already absorbed everything.

You can use this deliberately with a bare star:

def create_user(name, *, admin=False):
    ...

create_user("Asha", admin=True)   # fine
create_user("Asha", True)         # TypeError

This forces callers to name the flag, so a reader never has to guess what a bare True means. It is a genuinely good habit for boolean parameters, where f(x, True, False) is unreadable at the call site.

Where you will actually meet them

The most common real use is forwarding arguments through a wrapper without caring what they are:

def log_calls(fn):
    def wrapper(*args, **kwargs):
        print(f"calling {fn.__name__}")
        return fn(*args, **kwargs)
    return wrapper

The wrapper accepts anything and passes it straight through. Every decorator you write will have this shape, which is why understanding both directions of the stars matters — the definition collects, the inner call spreads.

The same pattern appears in class inheritance:

class Base:
    def __init__(self, a, b): ...

class Child(Base):
    def __init__(self, *args, extra=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.extra = extra

The child adds a parameter without needing to know or repeat the parent's signature — so it does not break when the parent gains a field.

When not to use them

They are easy to overuse, and the cost is real.

A function declared def process(*args, **kwargs) tells a reader nothing about what it expects. Autocomplete cannot help, type checkers cannot help, and the only way to find out is to read the body. Explicit parameters are documentation.

Use them when the count genuinely varies, when forwarding to something else, or when a subclass should not duplicate a parent's signature. Do not use them to avoid deciding what a function needs.

One related trap worth repeating: a mutable default such as def f(items=[]) is created once and shared across calls. That is unrelated to the stars but bites the same people at the same stage — see OOP in Python for the full explanation.

Frequently Asked Questions

What is the difference between *args and **kwargs? *args collects extra positional arguments into a tuple; **kwargs collects extra keyword arguments into a dictionary. One is by position, the other by name.
Do I have to call them args and kwargs? No, only the stars are syntax. The names are convention, and keeping them makes your code instantly readable to other Python developers.
What does a bare * in a signature mean? Everything after it must be passed by keyword. It is a good way to force callers to name boolean flags, which makes call sites far more readable.
Why does passing a list directly fail? Passing the list gives the function one argument that happens to be a list. Prefixing it with * spreads its elements into separate arguments, which is usually what you meant.
Is it bad practice to use them everywhere? Yes. A signature of just *args and **kwargs hides what the function needs, defeats autocomplete and type checking, and forces readers into the body. Be explicit unless the count genuinely varies.