Skip to content

*args and **kwargs in Python

Mastering *args and **kwargs in Python: A Comprehensive Guide

Sometimes you do not know in advance how many arguments a function will receive — think of print()print(), which accepts any number of values. Python solves this with *args*args and **kwargs**kwargs, which let a function accept an arbitrary number of positional and keyword arguments. In this guide, we’ll explore packing and unpacking, the correct argument order, and how to pass arguments between functions.

Recap: Positional and Keyword Arguments

Before arbitrary arguments, recall the two basic kinds:

  • Positional arguments are matched by their position: add(2, 3)add(2, 3).
  • Keyword arguments are matched by name: add(x=2, y=3)add(x=2, y=3).

*args*args and **kwargs**kwargs extend these so a function can take as many as the caller passes.

*args – Arbitrary Positional Arguments

Putting a single ** before a parameter name tells Python to pack all extra positional arguments into a tuple. By convention the parameter is named argsargs, but any name works.

The syntax of *args*args in Python is as follows:

Syntax
def function_name(*args):
    # args is a tuple of all positional arguments
Syntax
def function_name(*args):
    # args is a tuple of all positional arguments
args_basic.py
def add_all(*args):
    print("args is:", args)     # a tuple
    return sum(args)
 
print(add_all(1, 2, 3))
print(add_all(10, 20, 30, 40))
args_basic.py
def add_all(*args):
    print("args is:", args)     # a tuple
    return sum(args)
 
print(add_all(1, 2, 3))
print(add_all(10, 20, 30, 40))

Output:

command
C:\Users\Your Name> python args_basic.py
args is: (1, 2, 3)
6
args is: (10, 20, 30, 40)
100
command
C:\Users\Your Name> python args_basic.py
args is: (1, 2, 3)
6
args is: (10, 20, 30, 40)
100

**kwargs – Arbitrary Keyword Arguments

Putting a double **** before a parameter name packs all extra keyword arguments into a dictionary. By convention it is named kwargskwargs.

The syntax of **kwargs**kwargs in Python is as follows:

Syntax
def function_name(**kwargs):
    # kwargs is a dict of all keyword arguments
Syntax
def function_name(**kwargs):
    # kwargs is a dict of all keyword arguments
kwargs_basic.py
def build_profile(**kwargs):
    print("kwargs is:", kwargs)    # a dict
    for key, value in kwargs.items():
        print(f"{key}: {value}")
 
build_profile(name="Alice", age=30, city="Paris")
kwargs_basic.py
def build_profile(**kwargs):
    print("kwargs is:", kwargs)    # a dict
    for key, value in kwargs.items():
        print(f"{key}: {value}")
 
build_profile(name="Alice", age=30, city="Paris")

Output:

command
C:\Users\Your Name> python kwargs_basic.py
kwargs is: {'name': 'Alice', 'age': 30, 'city': 'Paris'}
name: Alice
age: 30
city: Paris
command
C:\Users\Your Name> python kwargs_basic.py
kwargs is: {'name': 'Alice', 'age': 30, 'city': 'Paris'}
name: Alice
age: 30
city: Paris

Combining Everything

You can mix regular parameters, *args*args, default parameters, and **kwargs**kwargs in one function. The order is fixed:

Syntax
def function(positional, *args, keyword_default=value, **kwargs):
Syntax
def function(positional, *args, keyword_default=value, **kwargs):

The required order is:

  1. Standard positional parameters
  2. *args*args
  3. Keyword-only / default parameters
  4. **kwargs**kwargs
combined.py
def order(main, *extras, sauce="ketchup", **details):
    print("Main:", main)
    print("Extras:", extras)
    print("Sauce:", sauce)
    print("Details:", details)
 
order("Burger", "Fries", "Coke", sauce="mayo", size="large", spicy=True)
combined.py
def order(main, *extras, sauce="ketchup", **details):
    print("Main:", main)
    print("Extras:", extras)
    print("Sauce:", sauce)
    print("Details:", details)
 
order("Burger", "Fries", "Coke", sauce="mayo", size="large", spicy=True)

Output:

command
C:\Users\Your Name> python combined.py
Main: Burger
Extras: ('Fries', 'Coke')
Sauce: mayo
Details: {'size': 'large', 'spicy': True}
command
C:\Users\Your Name> python combined.py
Main: Burger
Extras: ('Fries', 'Coke')
Sauce: mayo
Details: {'size': 'large', 'spicy': True}

Diagram:

diagram argument packing mermaid
How arguments are distributed into parameters

Unpacking with * and **

The ** and **** operators also work in the opposite direction. When calling a function, ** unpacks a list/tuple into positional arguments and **** unpacks a dict into keyword arguments.

unpacking.py
def introduce(name, age, city):
    print(f"{name} is {age} and lives in {city}")
 
# Unpack a list into positional arguments
data = ["Bob", 25, "London"]
introduce(*data)
 
# Unpack a dict into keyword arguments
info = {"name": "Carol", "age": 28, "city": "Berlin"}
introduce(**info)
unpacking.py
def introduce(name, age, city):
    print(f"{name} is {age} and lives in {city}")
 
# Unpack a list into positional arguments
data = ["Bob", 25, "London"]
introduce(*data)
 
# Unpack a dict into keyword arguments
info = {"name": "Carol", "age": 28, "city": "Berlin"}
introduce(**info)

Output:

command
C:\Users\Your Name> python unpacking.py
Bob is 25 and lives in London
Carol is 28 and lives in Berlin
command
C:\Users\Your Name> python unpacking.py
Bob is 25 and lives in London
Carol is 28 and lives in Berlin

Forwarding Arguments

A common pattern is a wrapper function that accepts anything and passes it straight through to another function. This is how decorators wrap arbitrary functions.

forwarding.py
def logged(func, *args, **kwargs):
    print(f"Calling {func.__name__} with {args} {kwargs}")
    return func(*args, **kwargs)     # forward everything
 
def power(base, exponent):
    return base ** exponent
 
print(logged(power, 2, exponent=10))
forwarding.py
def logged(func, *args, **kwargs):
    print(f"Calling {func.__name__} with {args} {kwargs}")
    return func(*args, **kwargs)     # forward everything
 
def power(base, exponent):
    return base ** exponent
 
print(logged(power, 2, exponent=10))

Output:

command
C:\Users\Your Name> python forwarding.py
Calling power with (2,) {'exponent': 10}
1024
command
C:\Users\Your Name> python forwarding.py
Calling power with (2,) {'exponent': 10}
1024

Visualize it

When a function is called, positional extras are collected into *args*args as a tuple, and keyword extras are collected into **kwargs**kwargs as a dict.

diagram Binding f(1, 2, x=3, y=4) mermaid
Positional args flow into the *args tuple; keyword args flow into the **kwargs dict.

Conclusion

*args*args and **kwargs**kwargs let functions accept any number of arguments: *args*args packs extra positional arguments into a tuple, and **kwargs**kwargs packs extra keyword arguments into a dictionary. The same ** and **** operators unpack sequences and dictionaries back into arguments at the call site. Combine them with regular parameters in the fixed order — positional, *args*args, keyword defaults, **kwargs**kwargs — and use them to write flexible wrappers and decorators. For more hands-on examples and in-depth tutorials, explore our resources on Python Central Hub!


Try it: *args and **kwargs Exercises

Exercise 1 – Collect with *args

Exercise 2 – Collect with **kwargs

Exercise 3 – Unpacking at the Call Site

If this helped you, consider buying me a coffee ☕

Buy me a coffee

Was this page helpful?

Let us know how we did