You have each blog post's tags as a list, and you want all the tags in one list:
tag_lists = [["python", "web"], [], ["web", "testing"]]
Python 3.15 lets you unpack inside a comprehension. *tags adds each item of
tags to the result, as it does in a list display such as [*a, *b]:
all_tags = [*tags for tags in tag_lists]
print(all_tags) # ['python', 'web', 'web', 'testing']
Before 3.15 you write two for clauses, which run left to right like two
nested for loops:
all_tags = [tag for tags in tag_lists for tag in tags]
Both give the same list. The new form says "the items of each tags", and it
needs no name for the inner item.
Sets and dictionaries work too
* works the same way in a set comprehension, and ** merges dictionaries
in a dict comprehension:
unique_tags = {*tags for tags in tag_lists}
# {'python', 'web', 'testing'}
layers = [{"host": "localhost", "port": 8000}, {"port": 9000}]
config = {**layer for layer in layers}
# {'host': 'localhost', 'port': 9000}
- The list keeps every item, in order, duplicates included.
- The set drops duplicates. Its items must be hashable, and its order is not reliable.
- The dict merges the mappings in order. A later value replaces an earlier one, and the key keeps the position where it first appeared.
Unpacking flattens one level only. [*row for row in [[1, [2, 3]], [4]]]
gives [1, [2, 3], 4].
With parentheses instead of brackets, you get a generator, which produces
the items one at a time without building a list. So
sum(*batch for batch in batches) adds up every number in every batch.
Where the star goes changes the result
groups = [[1, 2], [3]]
[*group for group in groups] # [1, 2, 3], one flat list
[(*group,) for group in groups] # [(1, 2), (3,)], one tuple per group
Two other mistakes give a clear SyntaxError:
**works only in a dict comprehension.[**layer for layer in layers]fails with "cannot use dict unpacking in list comprehension".- To unpack a conditional expression, wrap it in parentheses, as in
[*(a if use_a else b) for a, b in pairs].
Check your understanding
With batches = [[3, 4], [5]], what does print(*batch for batch in batches) print on Python 3.15?
Show answer
Hide answer
It prints a generator object, such as <generator object <genexpr> at 0x...>,
and not 3 4 5. The star belongs to the generator expression, so print
gets one argument, the generator. sum(*batch for batch in batches) works for
the same reason, because sum takes one iterable. To pass every item as a
separate argument, put the generator in a variable first, then call
print(*items).
On older versions
Python 3.14 and older reject the new form with
SyntaxError: iterable unpacking cannot be used in comprehension. A version
check in the same file cannot hide it, because Python reads the whole file
before it runs any of it.
Write Code That Runs on Several Python Versions
explains your choices. To support older versions, use two for clauses, or
itertools.chain.from_iterable(tag_lists), which produces the items one at a
time like the generator form.
This site runs exercise code on your machine, or on a machine you linked, with the Python that the runtime there uses. Use the new form in an exercise only if that Python is 3.15 or newer.
Check your understanding
Your project now requires Python 3.15. Should you replace every chain.from_iterable call with the new form?
Show answer
Hide answer
Only where it reads better. Both forms are correct, so a blanket rewrite
changes many lines and fixes nothing. If you do switch, keep producing the
items one at a time.
chain.from_iterable(groups) produces items one at a time, so its match is
the generator (*g for g in groups), not the list [*g for g in groups].
Going further
The change comes from PEP 798. Async comprehensions support it too, as in
[*chunk async for chunk in stream], where stream is an async iterator
that yields lists.