Python Merge Lists with Unpacking
python merge lists with unpacking: Learn how to merge lists in Python using the unpacking operator, including syntax, edge cases, and practical patterns for combining...
python merge lists with unpacking requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
Merging lists is a common operation in Python, and the unpacking operator * provides a concise way to combine multiple lists into one. When you write [*a, *b], Python expands each list into its elements inside a new list literal, producing a merged list without calling a method or using extend. This syntax is readable, flexible, and works with any iterable, not just lists.
What Unpacking Does When Merging Lists
The unpacking operator * in a list literal tells Python to take the elements of an iterable and place them as individual items in the new list. This is distinct from simply nesting the list, which would create a list of lists.
first = [1, 2, 3] second = [4, 5, 6] merged = [*first, *second] print(merged) # [1, 2, 3, 4, 5, 6]
Without the asterisk, [first, second] would produce [[1, 2, 3], [4, 5, 6]]. The unpacking operator flattens the input lists into the new list, preserving the order of elements as they appear.
This behavior is not limited to lists. Any iterable, such as tuples, sets, or generators, can be unpacked into a list literal. For example:
tuple_a = (7, 8) set_b = {9, 10} merged = [*tuple_a, *set_b] print(merged) # [7, 8, 9, 10] (order of set may vary)
Because sets are unordered, the final list order is not guaranteed when unpacking a set. For deterministic results, stick to ordered iterables like lists or tuples.
Merging Two or More Lists with the Star Operator
The most straightforward use case is merging two lists, but the syntax scales naturally to any number of lists. You can also mix in individual elements.
a = [1, 2] b = [3, 4] c = [5, 6] combined = [*a, *b, *c] print(combined) # [1, 2, 3, 4, 5, 6]
You can insert literal values between unpacked lists:
result = [0, *a, 2.5, *b] print(result) # [0, 1, 2, 2.5, 3, 4]
This is particularly useful when you need to prepend or append a fixed value without calling insert or append repeatedly.
The unpacking approach creates a new list each time. The original lists remain unchanged, which is important when you need to preserve the source data.
How Unpacking Differs from Other Merge Methods
Python offers several ways to merge lists, and each has different characteristics. The unpacking method is often the most readable, but it is not always the best choice depending on the context.
| Method | Syntax | Mutates Original? | Creates New List? | Use Case |
|---|---|---|---|---|
| Unpacking | [*a, *b] | No | Yes | Concise, readable merging |
+ operator | a + b | No | Yes | Simple two-list merge |
extend | a.extend(b) | Yes | No | In-place modification |
list + generator | list(chain(a, b)) | No | Yes | Lazy evaluation or many iterables |
For merging exactly two lists, a + b is equivalent in result and slightly shorter. However, unpacking becomes clearer when merging more than two lists or when you need to insert literals between them. For example, [*a, 0, *b] is more readable than a + [0] + b because the intent is explicit.
The extend method is the right choice when you want to modify an existing list rather than create a new one. It avoids allocating a second list, which can matter in memory-constrained environments.
When Unpacking Fails or Behaves Unexpectedly
Unpacking is straightforward, but there are a few edge cases to keep in mind.
Unpacking a Single Iterable
If you write [*a], you get a shallow copy of a. This is a common idiom for copying a list, but it is not always obvious. The copy is shallow, so nested objects are still shared.
original = [[1, 2], 3] copy = [*original] copy[0].append(99) print(original) # [[1, 2, 99], 3]
Unpacking a String
Strings are iterable, so unpacking a string produces a list of characters.
chars = [*"abc"] print(chars) # ['a', 'b', 'c']
This is useful for splitting a string into characters, but it can be surprising if you expected the string to be treated as a single element.
Unpacking a Generator
Unpacking a generator consumes it completely. If you need to reuse the generator later, you must recreate it.
gen = (x for x in range(3)) merged = [*gen] print(merged) # [0, 1, 2] print(list(gen)) # [] because the generator is exhausted
Unpacking a Dictionary
Unpacking a dictionary with * yields its keys, not its key-value pairs. To merge dictionaries, use ** instead.
d1 = {"a": 1} d2 = {"b": 2} keys = [*d1, *d2] print(keys) # ['a', 'b']
Memory and Performance Considerations
When you use unpacking to merge lists, Python allocates a new list and copies references to the elements from the source lists. This means the operation runs in O(n) time, where n is the total number of elements, and uses memory proportional to the combined size.
For small or medium lists, the overhead is negligible. However, if you are merging very large lists in a loop, repeatedly creating new lists can lead to unnecessary allocation and copying. In such cases, consider using extend on a pre-allocated list or using itertools.chain to iterate lazily without building a full list.
from itertools import chain large_a = range(1000000) large_b = range(1000000) # Lazy iteration, no new list created for item in chain(large_a, large_b): process(item)
If you need a concrete list and the number of source lists is dynamic, unpacking inside a list comprehension is not directly possible. Instead, you can use list(chain.from_iterable(list_of_lists)) to flatten a list of lists efficiently.
Practical Patterns for Real-World Code
Unpacking is particularly useful when you need to combine lists with fixed delimiters or when the number of lists is known at write time. For example, building a list of command-line arguments:
base_args = ["--verbose", "--config", "app.conf"] extra_args = ["--port", "8080"] all_args = [*base_args, *extra_args]
Another pattern is merging multiple lists returned from different functions into a single list for further processing:
def get_users(): return ["alice", "bob"] def get_admins(): return ["carol"] all_users = [*get_users(), *get_admins()]
When you need to conditionally include a list, you can use unpacking inside a conditional expression:
primary = [1, 2] optional = [3, 4] if include_optional else [] combined = [*primary, *optional]
This keeps the logic compact and avoids mutating the original lists.
For merging an arbitrary number of lists stored in a variable, you can use functools.reduce or itertools.chain, but unpacking is not designed for dynamic counts. The * operator requires the lists to be written explicitly in the literal. If you have a list of lists, list(chain.from_iterable(lists)) is the idiomatic approach.
Unpacking also works well with tuple literals, allowing you to merge lists and tuples into a single tuple:
tuple_a = (1, 2) list_b = [3, 4] combined_tuple = (*tuple_a, *list_b) print(combined_tuple) # (1, 2, 3, 4)
This symmetry makes the unpacking syntax a consistent tool across Python's sequence types.