Master Python’s Unpacking Operators: * and ** for Lists, Dictionaries, and Functions
This guide explores Python’s unpacking operators * and **, showing how they simplify printing, assigning, merging lists and dictionaries, handling variable function arguments, creating dynamic logging functions, and performing nested unpacking, while also offering practical tips to avoid common pitfalls.
Today I want to share my experience using unpacking operators — * for sequences, ** for dictionaries.
What is the unpacking operator?
These operators help quickly unpack lists, tuples, and dictionaries. I mainly use * for sequences and ** for dictionaries.
Unpacking lists and tuples
Before learning the * operator, I usually used a loop to print each element, for example:
<code>numbers = [1, 2, 3, 4]
for num in numbers:
print(num)
</code>Now I can accomplish the same with a single line of code:
<code>numbers = [1, 2, 3, 4]
print(*numbers)
</code>The output is:
<code>1 2 3 4
</code>This makes the code more concise.
Understanding this — unpacking is not only for printing; it can also be used in assignment. I can split a list into multiple parts with a single line of code, which is amazing, right?
For example:
<code>first, *middle, last = [1, 2, 3, 4, 5]
</code>The output is:
<code>first = 1
middle = [2, 3, 4]
last = 5
</code>In this example, first takes the first element, last takes the last element, and middle captures all the middle elements. This technique is very useful when we need to split data into multiple parts.
Merging lists using unpacking
We often need to merge lists. Previously I usually used the + operator, for example:
<code>list1 = [1, 2, 3]
list2 = [4, 5, 6]
merged = list1 + list2
print(merged)
</code>But using unpacking operators, this process becomes more flexible:
<code>list1 = [1, 2, 3]
list2 = [4, 5, 6]
merged = [0, *list1, *list2, 7]
print(merged)
</code>The output is:
<code>[0, 1, 2, 3, 4, 5, 6, 7]
</code>Now I can easily add extra elements while merging, giving me more flexible control over the data.
Merging dictionaries using **
Previously I merged dictionaries like this:
<code>dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged = dict1.copy()
merged.update(dict2)
print(merged)
</code>Now I can merge with a single line of code:
<code>dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged = {**dict1, **dict2}
print(merged)
</code>Note that if the dictionaries share the same key, the value from the second dictionary overwrites the first, which helps understand which values are retained during merging.
Using unpacking operators in function calls
We often write functions whose number of parameters may vary. Using unpacking operators, I can pass any number of arguments without extra hassle.
Using *args
Now I usually use *args when I'm unsure how many positional arguments a function may need. For example:
<code>def add_numbers(*args):
total = sum(args)
print("Total:", total)
add_numbers(1, 2, 3, 4)
</code>This makes the function very flexible. I can pass any number of numbers without modifying the function.
Using **kwargs
Similarly, ** lets me handle named parameters. This is perfect when I need to add extra options to a function:
<code>def greet(name, **kwargs):
greeting = kwargs.get("greeting", "Hello")
punctuation = kwargs.get("punctuation", "!")
print(f"{greeting}, {name}{punctuation}")
greet("Kiran", greeting="Hi", punctuation="!!!")
</code>Now I can pass extra information, and the function extracts what it needs, making calls cleaner.
Implementing dynamic function parameters with unpacking operators
I recall writing a logging function that needed many optional parameters without cluttering the code. In this case, unpacking operators were a great help:
<code>def log_message(message, **options):
level = options.get("level", "INFO")
timestamp = options.get("timestamp", "N/A")
extra = options.get("extra", "")
print(f"[{level}] {timestamp}: {message}{extra}")
log_message("System started.", level="DEBUG", timestamp="2025-03-01 10:00", extra="Initialization complete.")
</code>In this way, my logging function can be adjusted according to different needs.
Nested unpacking
Python supports nested unpacking, which is useful for handling complex data structures.
For example:
<code>data = [("Kiran", 25), ("Mann", 30), ("Grewal", 35)]
for name, age in data:
print(f"{name} is {age} years old.")
</code>I can also mix unpacking in more complex structures, for example:
<code>records = [("Kiran", [85, 90, 95]), ("Mann", [70, 80, 90])]
for name, scores in records:
first, *middle, last = scores
print(f"{name}'s first score: {first}, last score: {last}, other scores: {middle}")
</code>The output is:
<code>Kiran's first score: 85, last score: 95, other scores: [90]
Mann's first score: 70, last score: 90, other scores: [80]
</code>Using unpacking for function parameters
When a function returns multiple values, I use unpacking to store them in separate variables:
<code>def get_stats(numbers):
total = sum(numbers)
count = len(numbers)
average = total / count if count else 0
return total, count, average
total, count, average = get_stats([10, 20, 30, 40])
print("Total:", total, "Count:", count, "Average:", average)
</code>The output is:
<code>Total: 100 Count: 4 Average: 25.0
</code>This makes the code easier to read and maintain.
Things to watch out for when using
Although I like using these operators, I use them cautiously. Keep the following points in mind:
Do not use too many unpacking operators in a single line, as it can make the code hard to read. Always ask if others can understand it easily.
When merging dictionaries, if the same key appears in multiple dictionaries, the value from the last dictionary overwrites earlier ones. Check for potential conflicts when needed.
Ensure the data format being unpacked matches expectations, otherwise the code may crash.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.