Introduction: Argument Unpacking With *args and **kwargs
Sometimes a function is designed to accept a variable number of arguments using *args or **kwargs. When you already have those values stored in a collection, you can unpack the collection when calling the function.
This process of Python argument unpacking with *args and **kwargs expands values from existing collections into individual positional or keyword arguments during the function call.
The * operator can unpack an iterable into positional arguments that *args collects, while the ** operator can unpack a dictionary into keyword arguments that **kwargs collects.
This makes it possible to pass existing lists, tuples, other iterables and dictionaries to functions that accept a variable number of arguments.
Unpacking Positional Arguments Into *args
When a function uses *args, you can use * to unpack an iterable and pass its values as separate positional arguments.
Unpacking a List Into *args
A list can be unpacked with * when its values need to be passed to *args.
def show_values(*args):
print(args)
values = [10, 20, 30]
show_values(*values)
# Output:
(10, 20, 30)
Explanation: The *values expression takes the three values from the list and passes them as separate positional arguments.
The *args parameter then collects those arguments into a tuple.
Note: The call is equivalent to:
show_values(10, 20, 30)
Unpacking a Tuple Into *args
A tuple can be unpacked in the same way. Each tuple value becomes a separate positional argument.
def show_values(*args):
print(args)
values = (10, 20, 30)
show_values(*values)
# Output:
(10, 20, 30)
Explanation: The *values expression unpacks the tuple, and *args collects the resulting positional arguments into a tuple.
Unpacking Other Iterables Into *args
The * operator can also unpack other iterables, such as strings and sets, into positional arguments.
def show_values(*args):
print(args)
letters = "ABC"
show_values(*letters)
# Output:
('A', 'B', 'C')
Explanation: The string is iterable, so *letters passes its characters as separate positional arguments. The *args parameter collects them into a tuple.
Unpacking Keyword Arguments Into **kwargs
When a function uses **kwargs, you can use ** to unpack a dictionary and pass its entries as separate keyword arguments.
Unpacking a Dictionary Into **kwargs
When a dictionary is unpacked with **, its keys become keyword argument names and its values become the corresponding argument values.
def show_details(**kwargs):
print(kwargs)
details = {
"name": "Alice",
"age": 25
}
show_details(**details)
# Output:
{'name': 'Alice', 'age': 25}
Explanation: The **details expression passes name="Alice" and age=25 as keyword arguments. The **kwargs parameter then collects them into a dictionary.
Note: The call is equivalent to:
show_details(name="Alice", age=25)
Matching Dictionary Keys With **kwargs
Once the keyword arguments are collected, their values can be accessed through the kwargs dictionary.
def show_details(**kwargs):
print("Name:", kwargs["name"])
print("Age:", kwargs["age"])
details = {
"name": "Alice",
"age": 25
}
show_details(**details)
# Output:
Name: Alice
Age: 25
Explanation: The **kwargs parameter collects the keyword arguments into a dictionary. The keys "name" and "age" are then used to access their corresponding values.
Handling Unmatched or Invalid Keywords
A function that uses **kwargs can accept keyword arguments even when their names are not explicitly listed in the function definition.
def show_details(**kwargs):
print(kwargs)
details = {
"name": "Alice",
"city": "Delhi"
}
show_details(**details)
# Output:
{'name': 'Alice', 'city': 'Delhi'}
Explanation: The dictionary provides the keyword arguments name="Alice" and city="Delhi". Because the function uses **kwargs, both keywords are collected into the kwargs dictionary.
This is different from a function with fixed parameters. When a dictionary is unpacked into such a function, each key must match a parameter that the function can accept.
Combining * and ** During Argument Unpacking
Combining both types of arguments: Python argument unpacking with *args and **kwargs allows you to use both unpacking operators in the same function call. The function can then receive positional arguments with * and keyword arguments with **.
Unpacking Positional and Keyword Arguments Together
def display(*args, **kwargs):
print("Positional:", args)
print("Keyword:", kwargs)
values = [10, 20]
details = {
"name": "Alice",
"age": 25
}
display(*values, **details)
# Output:
Positional: (10, 20)
Keyword: {'name': 'Alice', 'age': 25}
Explanation: The *values expression supplies 10 and 20 as positional arguments, while
**details supplies name="Alice" and age=25 as keyword arguments.
Understanding The Execution Flow: The unpacking happens when the function is called. The function then receives the resulting positional arguments through args and the resulting keyword arguments through kwargs.
Using Regular Arguments With Unpacked Arguments
Regular positional and keyword arguments can also be used together with unpacked arguments.
def display(*args, **kwargs):
print("Positional:", args)
print("Keyword:", kwargs)
values = [20, 30]
details = {
"city": "Delhi"
}
display(10, *values, country="India", **details)
# Output:
Positional: (10, 20, 30)
Keyword: {'country': 'India', 'city': 'Delhi'}
Explanation: The regular value 10 and the values from *values are collected by *args. The regular keyword argument country="India" and the entries from **details are collected by **kwargs.
Common Rules and Errors in Argument Unpacking With *args and **kwargs
Python argument unpacking with *args and **kwargs must produce a valid set of arguments for the function call. Problems can occur when the unpacked values do not match what the function can accept.
Incorrect Number of Positional Arguments
When an iterable is unpacked with *, it provides one positional argument for each value. If the function requires a different number of positional arguments, Python raises a TypeError.
def add(a, b):
return a + b
values = [10, 20, 30]
add(*values)
# Error:
TypeError: add() takes 2 positional arguments but 3 were given
Explanation: The list contains three values, but the function accepts only two positional arguments.
Unexpected Keyword Arguments
When ** is used with a function that has fixed parameters, each dictionary key must be accepted by that function.
def introduce(name, age):
print(name, age)
person = {
"name": "Alice",
"city": "Delhi"
}
introduce(**person)
# Error:
TypeError: introduce() got an unexpected keyword argument 'city'
Explanation: The function accepts name and age, but the dictionary provides city. Because there is no matching parameter for city, Python raises a TypeError.
Duplicate Keyword Arguments
A keyword argument cannot be supplied more than once in the same function call.
def introduce(name, age):
print(name, age)
person = {
"name": "Alice"
}
introduce(**person, name="Bob", age=25)
# Error:
TypeError: got multiple values for keyword argument 'name'
Explanation: The dictionary supplies a value for name, and the regular keyword argument supplies another value for
the same parameter. Python therefore raises a TypeError.
The key rule is simple: * unpacks positional arguments into *args, while ** unpacks keyword arguments into
**kwargs. The resulting arguments must still form a valid function call.
Key Takeaways: Argument unpacking with *args and **kwargs
Here are the key points to remember about Python argument unpacking with *args and **kwargs:
*unpacks an iterable into separate positional arguments.*argscollects positional arguments into a tuple.**unpacks a dictionary into separate keyword arguments.**kwargscollects keyword arguments into a dictionary.*and**can be used together in the same function call.- Unpacked arguments must still form a valid function call for the target function.