Quick Answer

In Python, *args lets a function accept any number of positional arguments, which arrive as a tuple. **kwargs accepts any number of keyword arguments, which arrive as a dictionary. You can also use * and ** when calling a function to unpack a list or dict into separate arguments. The correct parameter order is: normal parameters, *args, keyword-only parameters, then **kwargs.

What are *args and **kwargs?

If you have read some Python code, you have probably seen function definitions like def func(*args, **kwargs): and wondered what the stars mean. This guide explains python args and kwargs in plain language, with examples you can run yourself.

The short version: *args and **kwargs let a function accept a flexible number of arguments instead of a fixed list. *args handles extra positional arguments; **kwargs handles extra keyword arguments.

One thing that trips up beginners: the names args and kwargs are just convention. The real work is done by the * and ** symbols. You could write *items or **options and it would behave exactly the same. Sticking to args and kwargs simply makes your code easier for other people to read.

How *args collects positional arguments

*args gathers any extra positional arguments into a tuple. This is useful when you do not know in advance how many values someone will pass.

def total(*args):
    print(args)          # args is a tuple
    return sum(args)

print(total(1, 2, 3))    # prints (1, 2, 3), returns 6
print(total(10, 20))     # prints (10, 20), returns 30
print(total())           # prints (), returns 0

Because args is a normal tuple, you can loop over it, index into it, or pass it to functions like sum() and len(). If no extra arguments are given, you simply get an empty tuple — no error is raised.

How **kwargs collects keyword arguments

**kwargs gathers any extra keyword arguments into a dictionary, where the keys are the argument names and the values are what was passed in.

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

show_profile(name="Aisha", city="Pune", course="Python")
# name: Aisha
# city: Pune
# course: Python

Since kwargs is a normal dictionary, you can use .get(), .items(), and in checks on it. It is a good fit for optional settings where every option has a clear name.

Unpacking: using * and ** when calling

The * and ** symbols also work in the opposite direction. When you call a function, you can use them to unpack a list or dictionary into separate arguments.

def greet(greeting, name):
    print(f"{greeting}, {name}!")

parts = ["Hello", "Ravi"]
greet(*parts)            # same as greet("Hello", "Ravi")

details = {"greeting": "Namaste", "name": "Priya"}
greet(**details)         # same as greet(greeting="Namaste", name="Priya")

So the same symbol has two jobs. In a definition it collects arguments; in a call it spreads them out. A single * unpacks a sequence (list or tuple) into positional arguments, and a double ** unpacks a dictionary into keyword arguments.

Getting the parameter order right

When you combine everything in one function, the order of the parameters matters. Python requires this exact sequence:

  1. Normal positional parameters
  2. *args
  3. Keyword-only parameters (with or without defaults)
  4. **kwargs
def register(name, *args, active=True, **kwargs):
    print("name:", name)
    print("args:", args)
    print("active:", active)
    print("kwargs:", kwargs)

register("Meera", 21, "Delhi", active=False, role="student")
# name: Meera
# args: (21, 'Delhi')
# active: False
# kwargs: {'role': 'student'}

Anything written after *args becomes keyword-only — you must pass it by name. If you get the order wrong, for example putting **kwargs before *args, Python raises a SyntaxError.

A practical use: wrapper and forwarding functions

The most common real-world use of *args and **kwargs together is forwarding arguments — writing a function that wraps another function without caring about its exact signature. Decorators rely on this pattern.

import time

def timed(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)      # forward everything
        elapsed = time.time() - start
        print(f"{func.__name__} took {elapsed:.4f}s")
        return result
    return wrapper

@timed
def add(a, b):
    return a + b

print(add(3, 5))        # runs add, prints the timing, returns 8

Notice how wrapper never needs to know that add takes two numbers. It accepts whatever arguments come in with *args, **kwargs, then passes them straight through using func(*args, **kwargs). The same wrapper would work on a function with five parameters or none. This "collect, then forward" pattern shows up everywhere in logging, caching, and timing code.

Want to practise patterns like this with guided, hands-on exercises? Our free Python course covers functions, decorators, and more, one step at a time.

Common gotchas to avoid

A few things commonly confuse beginners:

  • args is a tuple, kwargs is a dict. Remember it as: one star for a sequence of values, two stars for named pairs.
  • The names are just convention. *a and **kw work identically. Use args and kwargs so others instantly recognise your intent.
  • Positional values must come before keyword ones. Writing greet(name="Priya", "Hi") raises a SyntaxError because a positional argument follows a keyword argument.
  • Unpacking needs matching keys. greet(**details) fails if the dictionary has a key that is not a parameter name, or is missing a required one.
  • A bare * forces keyword-only arguments. def f(a, *, b): means b must always be passed by name, even though there is no *args at all.

When to use them (and when not to)

Reach for *args and **kwargs when flexibility genuinely helps — but do not overuse them.

  • Use *args when a function naturally takes a variable number of values, like a sum-style helper.
  • Use **kwargs for optional named settings, or to pass a bundle of options onward.
  • Use both together mainly for wrappers, decorators, and subclasses that forward arguments they should not need to know about.

Recommendation: if you know exactly which parameters a function needs, name them explicitly. Explicit parameters give clearer error messages, better editor autocomplete, and code that is easier to read. Save *args and **kwargs for the cases where that flexibility is the whole point.

Frequently Asked Questions

What is the difference between *args and **kwargs?

*args collects extra positional arguments into a tuple, while **kwargs collects extra keyword (named) arguments into a dictionary. Use one star for a list of values and two stars for name-value pairs.

Do I have to use the names "args" and "kwargs"?

No. The names are only a convention. *items and **options work exactly the same. The * and ** symbols do the real work, but sticking to args and kwargs makes your code familiar to other Python developers.

What order do *args and **kwargs go in?

The order is: normal parameters, then *args, then any keyword-only parameters, then **kwargs. Putting them in the wrong order — for example **kwargs before *args — causes a SyntaxError.

What does * do when I call a function instead of defining one?

In a function call, a single * unpacks a list or tuple into separate positional arguments, and ** unpacks a dictionary into keyword arguments. So greet(*["Hi", "Ravi"]) is the same as greet("Hi", "Ravi").

Can a function use *args and **kwargs at the same time?

Yes, and it is very common. A function like def wrapper(*args, **kwargs): accepts any combination of positional and keyword arguments, which makes it ideal for decorators and wrapper functions that forward everything to another function.