Unpacking with * and **

Spread sequences and mappings with * and ** — in calls, assignments, literals and returns.

The * and ** operators do double duty. In a function definition they collect arguments (that is *args/**kwargs). In an expression they do the opposite: they unpack a sequence or mapping into its parts.

Star unpacking in assignment

Grab the ends and keep the middle in one line — the starred name always becomes a list:

first, *middle, last = [1, 2, 3, 4, 5]
print(first)    # 1
print(middle)   # [2, 3, 4]
print(last)     # 5

Spreading into literals and calls

  • Merge lists: [*a, *b]. Merge dicts: {**a, **b} (later keys win).
  • Forward arguments: func(*args, **kwargs).
  • Build a call from data: point(*coords) unpacks a tuple into positional arguments.

Dict merging with {**a, **b} is the clean, non-mutating way to combine configurations — the original dictionaries are untouched.

Example

Example · python
# 1) Star assignment: split head, body and tail of a log line
level, *message_parts, timestamp = 'ERROR disk is full at 09:41'.split()
print('level    :', level)
print('message  :', ' '.join(message_parts))
print('timestamp:', timestamp)

# 2) Layered configuration without mutating anything
defaults = {'host': 'localhost', 'port': 8000, 'debug': False}
user_cfg = {'port': 9090, 'debug': True}
config = {**defaults, **user_cfg, 'source': 'merged'}
print('config   :', config)
print('defaults untouched:', defaults['port'])   # still 8000

# 3) Unpack a record straight into a function call
def make_url(scheme, host, port):
    return f'{scheme}://{host}:{port}'

parts = ('https', 'example.com', 443)
print(make_url(*parts))

# 4) Flatten nested data in a comprehension
rows = [[1, 2], [3, 4], [5, 6]]
flat = [x for row in rows for x in row]
print('flat     :', [*flat, 7])

# Output:
# level    : ERROR
# message  : disk is full at
# timestamp: 09:41
# config   : {'host': 'localhost', 'port': 9090, 'debug': True, 'source': 'merged'}
# defaults untouched: 8000
# https://example.com:443
# flat     : [1, 2, 3, 4, 5, 6, 7]

When to use it

  • A function call uses **config_dict to pass a dictionary of settings as keyword arguments.
  • A catch-all unpacking assigns the first and last items of a list to variables, collecting the middle with *.
  • Two lists are merged inline using [*list_a, *list_b] to build a combined list without mutating either.

More examples

Extended iterable unpacking with *

Uses * to collect all middle elements into a list while binding the ends to named variables.

Example · python
first, *middle, last = [1, 2, 3, 4, 5]
print(first)   # 1
print(middle)  # [2, 3, 4]
print(last)    # 5

Merge lists and dicts inline

Spreads two lists into a new list and merges two dicts, with the later dict winning on duplicate keys.

Example · python
a = [1, 2, 3]
b = [4, 5, 6]
combined = [*a, *b]
print(combined)   # [1, 2, 3, 4, 5, 6]

defaults = {'timeout': 30, 'retries': 3}
overrides = {'timeout': 10}
config = {**defaults, **overrides}
print(config)   # {'timeout': 10, 'retries': 3}

Unpack into function arguments

Unpacks a dict as keyword arguments and a tuple as positional arguments in function calls.

Example · python
def connect(host, port, timeout):
    print(f'Connecting to {host}:{port} (timeout={timeout}s)')

params = {'host': 'db.local', 'port': 5432, 'timeout': 10}
connect(**params)
# Connecting to db.local:5432 (timeout=10s)

args = ('cache.local', 6379, 5)
connect(*args)
# Connecting to cache.local:6379 (timeout=5s)

Discussion

  • Be the first to comment on this lesson.