Unpacking and *args/**kwargs
Python has compact syntax for pulling values out of collections, and for functions that accept a flexible number of arguments. Both patterns appear constantly in real code.
One collection, split into several named pieces.
1. Learning Objective
By the end of this session you will be able to:
- Unpack values from a tuple or list into named variables
- Unpack selected keys from a dictionary
- Use * to collect "the rest" of a sequence during unpacking
- Write a function that accepts *args and **kwargs and explain what each collects
- Use ** to spread a dictionary into a function call as keyword arguments
2. Pre-Coding Quiz
Answer these before reading the concept explanation or writing any code. It is fine to get these wrong โ that is the point. You need 4/5 to proceed to the lab.
Given name, region = "Kenya", "Africa", what is region?
("Kenya", "Africa") into the two names on the left, in order. name gets "Kenya", region gets "Africa".Given first, *rest = [1, 2, 3, 4], what is rest?
* collects "everything else" into a list. first takes the first item (1), and rest takes everything remaining as a list: [2, 3, 4].Given def total(*args): return sum(args), what does total(1, 2, 3) return, and what is args inside the function?
*args collects any number of positional arguments into a tuple named args inside the function. sum((1, 2, 3)) is 6.Given def describe(**kwargs): return kwargs, what does describe(name="Kenya", region="Africa") return?
**kwargs collects any number of keyword arguments into a dictionary. The keys are the argument names, the values are what was passed.You have data = {"name": "Kenya", "region": "Africa"} and a function def show(name, region): .... Which call passes both dict values as the correct named arguments?
**data spreads the dictionary's key-value pairs as keyword arguments โ equivalent to show(name="Kenya", region="Africa"). *data would instead iterate over the dict's keys, which is not what we want here.3. The Concept โ Unpacking and *args / **kwargs
*args gathers positional extras into a tuple; **kwargs gathers keyword extras into a dict.
Unpacking a tuple or list
You can assign multiple variables from a sequence in one line, as long as the number of names matches the number of values.
name, region = "Kenya", "Africa"
print(name) # "Kenya"
print(region) # "Africa"
# Works with lists too, and with more than 2 values
first, second, third = [10, 20, 30]
print(second) # 20
Collecting the rest with *
A single starred name absorbs however many values are left over, always as a list.
first, *rest = [1, 2, 3, 4]
print(first) # 1
print(rest) # [2, 3, 4]
first, *middle, last = [1, 2, 3, 4, 5]
print(middle) # [2, 3, 4]
print(last) # 5
Unpacking selected keys from a dictionary
You cannot unpack a dict positionally like a tuple, but you can pull out specific values using .get() or bracket access โ this is how you'll turn a raw dict into named locals in later sessions.
country = {"name": "Kenya", "region": "Africa", "population": 54000000}
name = country["name"]
region = country["region"]
print(name, region) # Kenya Africa
*args โ a function that accepts any number of positional arguments
Inside the function, args is a tuple containing everything the caller passed positionally.
def total(*args):
print(type(args), args)
return sum(args)
print(total(1, 2, 3)) # <class 'tuple'> (1, 2, 3) -> 6
print(total(10, 20)) # <class 'tuple'> (10, 20) -> 30
**kwargs โ a function that accepts any number of keyword arguments
Inside the function, kwargs is a dictionary of every keyword argument the caller passed.
def describe(**kwargs):
print(type(kwargs), kwargs)
return kwargs
describe(name="Kenya", region="Africa")
# <class 'dict'> {'name': 'Kenya', 'region': 'Africa'}
Spreading a dict into a call with **
Going the other direction: if you have a dictionary and a function that expects named parameters, ** unpacks it into matching keyword arguments.
def show(name, region):
print(f"{name} is in {region}")
data = {"name": "Kenya", "region": "Africa"}
show(**data) # equivalent to show(name="Kenya", region="Africa")
4. Lab
What you will build
A file called unpacking_lab.py.
Step-by-step instructions
Create the file and unpack a country tuple
# unpacking_lab.py
country_tuple = ("Kenya", "Africa", 54000000)
name, region, population = country_tuple
print(name, region, population)
Use * to split a list of country names into first and rest
names = ["Kenya", "Ghana", "Peru", "Japan"]
first, *rest = names
print("First:", first)
print("Rest:", rest)
Write a total_population function using *args
Call it with 2 numbers and then with 4, proving it works with any count.
def total_population(*populations):
return sum(populations)
print(total_population(54000000, 31000000))
print(total_population(54000000, 31000000, 33000000, 125000000))
Write a build_country function using **kwargs
Print the kwargs dict, then return it.
def build_country(**fields):
print("Received fields:", fields)
return fields
country = build_country(name="Kenya", region="Africa", population=54000000)
print(country["name"])
Spread a dict into a function call
Write a plain function with named parameters and call it using ** on a dict.
def summarize(name, region, population):
return f"{name} ({region}) โ pop. {population:,}"
data = {"name": "Kenya", "region": "Africa", "population": 54000000}
print(summarize(**data))
5. Expected Files Changed
| File | Action | Why |
|---|---|---|
unpacking_lab.py |
Created | Demonstrates unpacking, *rest, *args, **kwargs, and ** spreading. |
docs/sessions/session-09/index.html |
Created | This session document. |
6. Commit Checkpoint
Once the lab is complete and you can explain every line, make this exact commit:
git add unpacking_lab.py docs/sessions/session-09/index.html
git commit -m "session-09: unpack sequences and use *args/**kwargs for flexible functions"
7. Code Review Checklist
Go through your code line by line and check each item:
8. Post-Coding Quiz
Same 5 questions. Take it again now that you have written and run the code. You need 4/5 to mark this session complete.
Given name, region = "Kenya", "Africa", what is region?
("Kenya", "Africa") into the two names on the left, in order. name gets "Kenya", region gets "Africa".Given first, *rest = [1, 2, 3, 4], what is rest?
* collects "everything else" into a list. first takes the first item (1), and rest takes everything remaining as a list: [2, 3, 4].Given def total(*args): return sum(args), what does total(1, 2, 3) return, and what is args inside the function?
*args collects any number of positional arguments into a tuple named args inside the function. sum((1, 2, 3)) is 6.Given def describe(**kwargs): return kwargs, what does describe(name="Kenya", region="Africa") return?
**kwargs collects any number of keyword arguments into a dictionary. The keys are the argument names, the values are what was passed.You have data = {"name": "Kenya", "region": "Africa"} and a function def show(name, region): .... Which call passes both dict values as the correct named arguments?
**data spreads the dictionary's key-value pairs as keyword arguments โ equivalent to show(name="Kenya", region="Africa"). *data would instead iterate over the dict's keys, which is not what we want here.9. Reflection Questions
Think through these after the post-quiz. No right answer โ they are for discussion.
- Why does
*argsproduce a tuple while**kwargsproduces a dictionary? Does that difference make sense given how positional vs keyword arguments work? - What would happen if you tried to unpack
a, b = [1, 2, 3]โ three values into two names? Try it and read the error. - Where in the labs so far have you already used dictionary values without realizing it was a form of unpacking?
- Can you think of a real function signature (in any library you've used) that probably uses **kwargs internally?
10. What Breaks If This Knowledge Is Missing?
- Unpacking mismatches: Unpacking the wrong number of values (too many or too few names) raises a
ValueErrorat runtime. Understanding this session means you'll immediately recognise that error instead of being confused by it. - Flexible constructors (Layer 2 and 4): When we build classes and a mock-data layer, functions frequently need to accept a variable, evolving set of fields. **kwargs is exactly how you keep a function's signature stable while its data shape grows.
- Calling real library functions (Layer 7): The
requestslibrary and many others accept **kwargs-style configuration. Without this session, those function calls look like unexplainable magic.
11. What We Learned
Python concept mastered: Unpacking sequences, the * "rest" pattern, and *args/**kwargs for functions with flexible argument counts.
Unlocks: You can now write functions that accept a flexible, evolving set of inputs โ essential for the data layer we build in Layer 4.
Next session: Session 10 โ Modules and Imports. We will split our growing file into separate modules and learn Python's import system.