Advanced Python
Python `*args` and `**kwargs`
Two Separate Concepts
*args |
**kwargs |
|
|---|---|---|
| Full name | Arguments | Keyword Arguments |
| Stores | Any number of positional arguments | Any number of keyword arguments |
| Data type stored in | Tuple | Dictionary |
| Number of asterisks | One * |
Two ** |
The names
argsandkwargsare conventions — you can use any name (e.g.*values,**options). The asterisks are what matter.
*args — As a Parameter
Allows a function to accept any number of positional arguments:
def sum_items(*args):
print(args) # tuple of all passed arguments
print(sum(args))
sum_items(1, 2, 3) # (1, 2, 3) → 6
sum_items(1, 2, 3, 4) # (1, 2, 3, 4) → 10
sum_items(1) # (1,) → 1
sum_items() # () → 0
**kwargs — As a Parameter
Allows a function to accept any number of keyword arguments:
def show(**kwargs):
print(kwargs) # dictionary of all keyword arguments
show(k=2, a=3) # {'k': 2, 'a': 3}
Access a specific keyword argument:
def show(**kwargs):
print(kwargs["k"])
show(k=2, a=3) # 2
Using Both Together
def test(*args, **kwargs):
print(args) # tuple of positional args
print(kwargs) # dict of keyword args
test(1, 2, k=3)
# (1, 2)
# {'k': 3}
With a required parameter before *args
def test(p1, *args, **kwargs):
print(p1, args, kwargs)
test(4, 1, 2, s=1, hello=4, k=True)
# 4 (1, 2) {'s': 1, 'hello': 4, 'k': True}
test() # ❌ TypeError — p1 is required
Parameter order rule: required params → *args → **kwargs
* as an Argument — Unpacking an Iterable (Splat)
Use * when calling a function to unpack a list/tuple/string into individual positional arguments:
def sum_items(a, b, c):
return a + b + c
my_args = [4, 5, 7]
result = sum_items(*my_args) # same as sum_items(4, 5, 7)
print(result) # 16
Works on any ordered iterable:
sum_items(*[4, 5, 7]) # list ✅
sum_items(*(4, 5, 7)) # tuple ✅
Practical use — clean printing without brackets
values = [1, 2, 3, 4, 5]
print(values) # [1, 2, 3, 4, 5] — includes brackets
print(*values) # 1 2 3 4 5 — individual values, space-separated
This works because
print()itself uses*argsinternally — it accepts any number of positional arguments.
** as an Argument — Unpacking a Dictionary
Use ** when calling a function to unpack a dictionary into keyword arguments:
def sum_items(a, b, c):
print(a, b, c)
return a + b + c
kwargs = {"a": 5, "b": 5, "c": 10}
result = sum_items(**kwargs) # same as sum_items(a=5, b=5, c=10)
print(result) # 20
Using Both When Calling a Function
def test(p1, *args, **kwargs):
print(p1, args, kwargs)
values = [1, 2]
kw = {"s": 1, "hello": 4, "k": True}
test(4, *values, **kw)
# 4 (1, 2) {'s': 1, 'hello': 4, 'k': True}
Parameter vs Argument — Summary
| Context | Syntax | What it does |
|---|---|---|
| Parameter (defining a function) | *args |
Accepts unlimited positional args → stored as tuple |
| Parameter (defining a function) | **kwargs |
Accepts unlimited keyword args → stored as dict |
| Argument (calling a function) | *my_list |
Unpacks list/tuple into individual positional args |
| Argument (calling a function) | **my_dict |
Unpacks dict into individual keyword args |
Key Takeaways & Recap
| Concept | Summary |
|---|---|
*args as param |
Accepts any number of positional args; stored as a tuple |
**kwargs as param |
Accepts any number of keyword args; stored as a dict |
*iterable as arg |
Unpacks a list/tuple/string into separate positional arguments |
**dict as arg |
Unpacks a dictionary into separate keyword arguments |
Why ** for kwargs? |
Dicts are key-value pairs — two asterisks unpack key=value format |
| Parameter order | normal → *args → **kwargs |
| Real-world use | print(*values) works because print uses *args internally |