*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:
def function_name(*args):
# args is a tuple of all positional argumentsdef function_name(*args):
# args is a tuple of all positional argumentsdef 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))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:
C:\Users\Your Name> python args_basic.py
args is: (1, 2, 3)
6
args is: (10, 20, 30, 40)
100C:\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:
def function_name(**kwargs):
# kwargs is a dict of all keyword argumentsdef function_name(**kwargs):
# kwargs is a dict of all keyword argumentsdef 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")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:
C:\Users\Your Name> python kwargs_basic.py
kwargs is: {'name': 'Alice', 'age': 30, 'city': 'Paris'}
name: Alice
age: 30
city: ParisC:\Users\Your Name> python kwargs_basic.py
kwargs is: {'name': 'Alice', 'age': 30, 'city': 'Paris'}
name: Alice
age: 30
city: ParisCombining Everything
You can mix regular parameters, *args*args, default parameters, and **kwargs**kwargs in one function. The order is fixed:
def function(positional, *args, keyword_default=value, **kwargs):def function(positional, *args, keyword_default=value, **kwargs):The required order is:
- Standard positional parameters
*args*args- Keyword-only / default parameters
**kwargs**kwargs
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)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:
C:\Users\Your Name> python combined.py
Main: Burger
Extras: ('Fries', 'Coke')
Sauce: mayo
Details: {'size': 'large', 'spicy': True}C:\Users\Your Name> python combined.py
Main: Burger
Extras: ('Fries', 'Coke')
Sauce: mayo
Details: {'size': 'large', 'spicy': True}Diagram:
graph TD
A["order('Burger', 'Fries', 'Coke', sauce='mayo', size='large', spicy=True)"] --> B[main = 'Burger']
A --> C["extras = ('Fries', 'Coke')"]
A --> D[sauce = 'mayo']
A --> E["details = {size, spicy}"]
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.
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)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:
C:\Users\Your Name> python unpacking.py
Bob is 25 and lives in London
Carol is 28 and lives in BerlinC:\Users\Your Name> python unpacking.py
Bob is 25 and lives in London
Carol is 28 and lives in BerlinForwarding 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.
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))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:
C:\Users\Your Name> python forwarding.py
Calling power with (2,) {'exponent': 10}
1024C:\Users\Your Name> python forwarding.py
Calling power with (2,) {'exponent': 10}
1024Visualize 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.
flowchart LR
A["f(1, 2, x=3, y=4)"] --> B["1"]
A --> C["2"]
A --> D["x=3"]
A --> E["y=4"]
B --> F["*args = (1, 2)"]
C --> F
D --> G["**kwargs = {x: 3, y: 4}"]
E --> G
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 coffeeWas this page helpful?
Let us know how we did
