When processing data from APIs, files, or databases, you often receive structured data that needs to be split into individual variables. Unpacking lets you extract values from sequences and mappings in a single, readable operation instead of using multiple index accesses.

Unpacking allows extracting values from sequences (lists, tuples) and mappings (dicts) into separate variables in a single operation.

Basic Unpacking

point
basic_unpacking.py
Replay: real traced execution (multi-file project)
"""Basic unpacking examples"""

# Tuple unpacking
print("Tuple unpacking:")

point = (10, 20)
x, y = point
print(f"point = {point}")
print(f"x = {x}, y = {y}")

# Swap variables
a, b = 5, 10
print(f"\nBefore swap: a={a}, b={b}")
a, b = b, a
print(f"After swap: a={a}, b={b}")

# List unpacking
print("\nList unpacking:")

coordinates = [100, 200, 300]
x, y, z = coordinates
print(f"coordinates = {coordinates}")
print(f"x={x}, y={y}, z={z}")

# String unpacking
print("\nString unpacking:")

name = "Alice"
first, second, third, fourth, fifth = name
print(f"name = '{name}'")
print(f"Letters: {first}, {second}, {third}, {fourth}, {fifth}")

# Multiple assignment
print("\nMultiple assignment:")

# Single line assignment
a, b, c = 1, 2, 3
print(f"a={a}, b={b}, c={c}")

# From list
values = [10, 20, 30]
x, y, z = values
print(f"x={x}, y={y}, z={z}")

# Function returns
print("\nFunction returns:")

def get_user():
    return "Alice", 30, "alice@example.com"

name, age, email = get_user()
print(f"User: {name}, {age}, {email}")

def min_max(numbers):
    return min(numbers), max(numbers)

minimum, maximum = min_max([5, 2, 8, 1, 9])
print(f"Min: {minimum}, Max: {maximum}")

# Enumerate unpacking
print("\nEnumerate unpacking:")

fruits = ["apple", "banana", "cherry"]

for index, fruit in enumerate(fruits):
    print(f"  {index}: {fruit}")

# Dict items unpacking
print("\nDict items unpacking:")

user = {"name": "Bob", "age": 25, "city": "NYC"}

for key, value in user.items():
    print(f"  {key}: {value}")

# Zip unpacking
print("\nZip unpacking:")

names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]

for name, score in zip(names, scores):
    print(f"  {name}: {score}")

# Nested tuples
print("\nNested tuples:")

data = ("Alice", (30, "alice@example.com"))
name, (age, email) = data
print(f"name={name}, age={age}, email={email}")

# Error handling
print("\nError handling:")

try:
    a, b = [1, 2, 3]  # Too many values
except ValueError as e:
    print(f"Error: {e}")

try:
    a, b, c = [1, 2]  # Too few values
except ValueError as e:
    print(f"Error: {e}")

# Underscore for ignored values
print("\nUnderscore for ignored values:")

# Ignore some values
first, _, third = [1, 2, 3]
print(f"first={first}, third={third} (ignored middle)")

name, _, _, city = ["Alice", 30, "alice@example.com", "NYC"]
print(f"name={name}, city={city} (ignored age and email)")

# Practical example
print("\nPractical example:")

def parse_coordinate(coord_str):
    """Parse 'x,y' string into tuple"""
    return tuple(map(int, coord_str.split(',')))

coord_str = "100,200"
x, y = parse_coordinate(coord_str)
print(f"Parsed '{coord_str}': x={x}, y={y}")

# Process CSV line
csv_line = "Alice,30,Engineer"
name, age, job = csv_line.split(',')
print(f"CSV: name={name}, age={age}, job={job}")

"""Basic unpacking examples"""

# Tuple unpacking
print("Tuple unpacking:")

point = (3, 4)
x, y = point
print(f"point = {point}")
print(f"x = {x}, y = {y}")

# Swap variables
a, b = 5, 10
print(f"\nBefore swap: a={a}, b={b}")
a, b = b, a
print(f"After swap: a={a}, b={b}")

# List unpacking
print("\nList unpacking:")

coordinates = [100, 200, 300]
x, y, z = coordinates
print(f"coordinates = {coordinates}")
print(f"x={x}, y={y}, z={z}")

# String unpacking
print("\nString unpacking:")

name = "Alice"
first, second, third, fourth, fifth = name
print(f"name = '{name}'")
print(f"Letters: {first}, {second}, {third}, {fourth}, {fifth}")

# Multiple assignment
print("\nMultiple assignment:")

# Single line assignment
a, b, c = 1, 2, 3
print(f"a={a}, b={b}, c={c}")

# From list
values = [10, 20, 30]
x, y, z = values
print(f"x={x}, y={y}, z={z}")

# Function returns
print("\nFunction returns:")

def get_user():
    return "Alice", 30, "alice@example.com"

name, age, email = get_user()
print(f"User: {name}, {age}, {email}")

def min_max(numbers):
    return min(numbers), max(numbers)

minimum, maximum = min_max([5, 2, 8, 1, 9])
print(f"Min: {minimum}, Max: {maximum}")

# Enumerate unpacking
print("\nEnumerate unpacking:")

fruits = ["apple", "banana", "cherry"]

for index, fruit in enumerate(fruits):
    print(f"  {index}: {fruit}")

# Dict items unpacking
print("\nDict items unpacking:")

user = {"name": "Bob", "age": 25, "city": "NYC"}

for key, value in user.items():
    print(f"  {key}: {value}")

# Zip unpacking
print("\nZip unpacking:")

names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]

for name, score in zip(names, scores):
    print(f"  {name}: {score}")

# Nested tuples
print("\nNested tuples:")

data = ("Alice", (30, "alice@example.com"))
name, (age, email) = data
print(f"name={name}, age={age}, email={email}")

# Error handling
print("\nError handling:")

try:
    a, b = [1, 2, 3]  # Too many values
except ValueError as e:
    print(f"Error: {e}")

try:
    a, b, c = [1, 2]  # Too few values
except ValueError as e:
    print(f"Error: {e}")

# Underscore for ignored values
print("\nUnderscore for ignored values:")

# Ignore some values
first, _, third = [1, 2, 3]
print(f"first={first}, third={third} (ignored middle)")

name, _, _, city = ["Alice", 30, "alice@example.com", "NYC"]
print(f"name={name}, city={city} (ignored age and email)")

# Practical example
print("\nPractical example:")

def parse_coordinate(coord_str):
    """Parse 'x,y' string into tuple"""
    return tuple(map(int, coord_str.split(',')))

coord_str = "100,200"
x, y = parse_coordinate(coord_str)
print(f"Parsed '{coord_str}': x={x}, y={y}")

# Process CSV line
csv_line = "Alice,30,Engineer"
name, age, job = csv_line.split(',')
print(f"CSV: name={name}, age={age}, job={job}")

"""Basic unpacking examples"""

# Tuple unpacking
print("Tuple unpacking:")

point = (100, 200)
x, y = point
print(f"point = {point}")
print(f"x = {x}, y = {y}")

# Swap variables
a, b = 5, 10
print(f"\nBefore swap: a={a}, b={b}")
a, b = b, a
print(f"After swap: a={a}, b={b}")

# List unpacking
print("\nList unpacking:")

coordinates = [100, 200, 300]
x, y, z = coordinates
print(f"coordinates = {coordinates}")
print(f"x={x}, y={y}, z={z}")

# String unpacking
print("\nString unpacking:")

name = "Alice"
first, second, third, fourth, fifth = name
print(f"name = '{name}'")
print(f"Letters: {first}, {second}, {third}, {fourth}, {fifth}")

# Multiple assignment
print("\nMultiple assignment:")

# Single line assignment
a, b, c = 1, 2, 3
print(f"a={a}, b={b}, c={c}")

# From list
values = [10, 20, 30]
x, y, z = values
print(f"x={x}, y={y}, z={z}")

# Function returns
print("\nFunction returns:")

def get_user():
    return "Alice", 30, "alice@example.com"

name, age, email = get_user()
print(f"User: {name}, {age}, {email}")

def min_max(numbers):
    return min(numbers), max(numbers)

minimum, maximum = min_max([5, 2, 8, 1, 9])
print(f"Min: {minimum}, Max: {maximum}")

# Enumerate unpacking
print("\nEnumerate unpacking:")

fruits = ["apple", "banana", "cherry"]

for index, fruit in enumerate(fruits):
    print(f"  {index}: {fruit}")

# Dict items unpacking
print("\nDict items unpacking:")

user = {"name": "Bob", "age": 25, "city": "NYC"}

for key, value in user.items():
    print(f"  {key}: {value}")

# Zip unpacking
print("\nZip unpacking:")

names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]

for name, score in zip(names, scores):
    print(f"  {name}: {score}")

# Nested tuples
print("\nNested tuples:")

data = ("Alice", (30, "alice@example.com"))
name, (age, email) = data
print(f"name={name}, age={age}, email={email}")

# Error handling
print("\nError handling:")

try:
    a, b = [1, 2, 3]  # Too many values
except ValueError as e:
    print(f"Error: {e}")

try:
    a, b, c = [1, 2]  # Too few values
except ValueError as e:
    print(f"Error: {e}")

# Underscore for ignored values
print("\nUnderscore for ignored values:")

# Ignore some values
first, _, third = [1, 2, 3]
print(f"first={first}, third={third} (ignored middle)")

name, _, _, city = ["Alice", 30, "alice@example.com", "NYC"]
print(f"name={name}, city={city} (ignored age and email)")

# Practical example
print("\nPractical example:")

def parse_coordinate(coord_str):
    """Parse 'x,y' string into tuple"""
    return tuple(map(int, coord_str.split(',')))

coord_str = "100,200"
x, y = parse_coordinate(coord_str)
print(f"Parsed '{coord_str}': x={x}, y={y}")

# Process CSV line
csv_line = "Alice,30,Engineer"
name, age, job = csv_line.split(',')
print(f"CSV: name={name}, age={age}, job={job}")

  1. point ← (10, 20), x ← 10, y ← 20, a ← 5, b ← 10, coordinates ← [100, 200, 300]

    1"""Basic unpacking examples"""23# Tuple unpacking4print("Tuple unpacking:")56point→ (10, 20) = (10, 20)  #@point=(3, 4), (100, 200)7x→ 10, y→ 20 = point(10, 20)8print(f"point = {point(10, 20)}")9print(f"x = {x10}, y = {y20}")1011# Swap variables12a→ 5, b→ 10 = 5, 1013print(f"\nBefore swap: a={a5}, b={b10}")14a→ 10, b→ 5 = b, a15print(f"After swap: a={a10}, b={b5}")1617# List unpacking18print("\nList unpacking:")1920coordinates→ [100, 200, 300] = [100, 200, 300]21x→ 100, y→ 200, z→ 300 = coordinates[100, 200, 300]22print(f"coordinates = {coordinates[100, 200, 300]}")23print(f"x={x100}, y={y200}, z={z300}")2425# String unpacking26print("\nString unpacking:")2728name→ Alice = "Alice"29first→ A, second→ l, third→ i, fourth→ c, fifth→ e = nameAlice30print(f"name = '{nameAlice}'")31print(f"Letters: {firstA}, {secondl}, {thirdi}, {fourthc}, {fifthe}")3233# Multiple assignment34print("\nMultiple assignment:")3536# Single line assignment37a→ 1, b→ 2, c→ 3 = 1, 2, 338print(f"a={a1}, b={b2}, c={c3}")3940# From list41values→ [10, 20, 30] = [10, 20, 30]42x→ 10, y→ 20, z→ 30 = values[10, 20, 30]43print(f"x={x10}, y={y20}, z={z30}")4445# Function returns46print("\nFunction returns:")4748def get_user():49    return "Alice", 30, "alice@example.com"5051name, age, email = get_user()52print(f"User: {name}, {age}, {email}")
    outputTuple unpacking:
    point = (10, 20)
    x = 10, y = 20
    
    Before swap: a=5, b=10
    After swap: a=10, b=5
    
    List unpacking:
    coordinates = [100, 200, 300]
    x=100, y=200, z=300
    
    String unpacking:
    name = 'Alice'
    Letters: A, l, i, c, e
    
    Multiple assignment:
    a=1, b=2, c=3
    x=10, y=20, z=30
    
    Function returns:
  2. name ← Alice, age ← 30, email ← alice@example.com

    51name→ Alice, age→ 30, email→ alice@example.com = get_user()52print(f"User: {nameAlice}, {age30}, {emailalice@example.com}")5354def min_max(numbers):55    return min(numbers), max(numbers)5657minimum, maximum = min_max([5, 2, 8, 1, 9])58print(f"Min: {minimum}, Max: {maximum}")
    outputUser: Alice, 30, alice@example.com
  3. def min_max(numbers):

    54def min_max(numbers[5, 2, 8, 1, 9]):55    return min(numbers[5, 2, 8, 1, 9]), max(numbers)
  4. minimum ← 1, maximum ← 9, fruits ← ['apple', 'banana', 'cherry']

    57minimum→ 1, maximum→ 9 = min_max([5, 2, 8, 1, 9])58print(f"Min: {minimum1}, Max: {maximum9}")5960# Enumerate unpacking61print("\nEnumerate unpacking:")6263fruits→ ['apple', 'banana', 'cherry'] = ["apple", "banana", "cherry"]
    outputMin: 1, Max: 9
    
    Enumerate unpacking:
  5. for index, fruit in enumerate(fruits):

    pass 1 of 3
    65for index0, fruitapple in enumerate(fruits['apple', 'banana', 'cherry']):66    print(f"  {index0}: {fruitapple}")
    output  0: apple
    All 3 passes — pass 1 is the card above
    passindexfruit
    10apple
    21banana
    32cherry
  6. user ← {'name': 'Bob', 'age': 25, 'city': 'NYC'}

    68# Dict items unpacking69print("\nDict items unpacking:")7071user→ {'name': 'Bob', 'age': 25, 'city': 'NYC'} = {"name": "Bob", "age": 25, "city": "NYC"}
    output
    Dict items unpacking:
  7. for key, value in user.items():

    pass 1 of 3
    73for keyname, valueBob in user{'name': 'Bob', 'age': 25, 'city': 'NYC'}.items():74    print(f"  {keyname}: {valueBob}")
    output  name: Bob
    All 3 passes — pass 1 is the card above
    passkeyvalue
    1nameBob
    2age25
    3cityNYC
  8. names ← ['Alice', 'Bob', 'Charlie'], scores ← [85, 92, 78]

    76# Zip unpacking77print("\nZip unpacking:")7879names→ ['Alice', 'Bob', 'Charlie'] = ["Alice", "Bob", "Charlie"]80scores→ [85, 92, 78] = [85, 92, 78]
    output
    Zip unpacking:
  9. for name, score in zip(names, scores):

    pass 1 of 3
    82for nameAlice, score85 in zip(names['Alice', 'Bob', 'Charlie'], scores[85, 92, 78]):83    print(f"  {nameAlice}: {score85}")
    output  Alice: 85
    All 3 passes — pass 1 is the card above
    passnamescore
    1Alice85
    2Bob92
    3Charlie78
  10. data ← ('Alice', (30, 'alice@example.com')), name ← Alice, age ← 30

    85# Nested tuples86print("\nNested tuples:")8788data→ ('Alice', (30, 'alice@example.com')) = ("Alice", (30, "alice@example.com"))89name→ Alice, (age→ 30, email→ alice@example.com) = data('Alice', (30, 'alice@example.com'))90print(f"name={nameAlice}, age={age30}, email={emailalice@example.com}")9192# Error handling93print("\nError handling:")
    output
    Nested tuples:
    name=Alice, age=30, email=alice@example.com
    
    Error handling:
  11. except ValueError as e:

    96    a, b = [1, 2, 3]  # Too many values97except ValueError as e:98    print(f"Error: {etoo many values to unpack (expected 2)}")
    outputError: too many values to unpack (expected 2)
  12. except ValueError as e:

    101    a, b, c = [1, 2]  # Too few values102except ValueError as e:103    print(f"Error: {enot enough values to unpack (expected 3, got 2)}")
    outputError: not enough values to unpack (expected 3, got 2)
  13. first ← 1, _ ← 2, third ← 3, name ← Alice, city ← NYC, coord_str ← 100,200

    105# Underscore for ignored values106print("\nUnderscore for ignored values:")107108# Ignore some values109first→ 1, _→ 2, third→ 3 = [1, 2, 3]110print(f"first={first1}, third={third3} (ignored middle)")111112name→ Alice, _→ alice@example.com, _, city→ NYC = ["Alice", 30, "alice@example.com", "NYC"]113print(f"name={nameAlice}, city={cityNYC} (ignored age and email)")114115# Practical example116print("\nPractical example:")117118def parse_coordinate(coord_str):119    """Parse 'x,y' string into tuple"""120    return tuple(map(int, coord_str.split(',')))121122coord_str→ 100,200 = "100,200"123x, y = parse_coordinate(coord_str100,200)124print(f"Parsed '{coord_str}': x={x}, y={y}")
    output
    Underscore for ignored values:
    first=1, third=3 (ignored middle)
    name=Alice, city=NYC (ignored age and email)
    
    Practical example:
  14. def parse_coordinate(coord_str):

    118def parse_coordinate(coord_str100,200):119    """Parse 'x,y' string into tuple"""120    return tuple(map(int, coord_str100,200.split(',')))
  15. x ← 100, y ← 200, csv_line ← Alice,30,Engineer, name ← Alice, age ← 30

    122coord_str = "100,200"123x→ 100, y→ 200 = parse_coordinate(coord_str100,200)124print(f"Parsed '{coord_str100,200}': x={x100}, y={y200}")125126# Process CSV line127csv_line→ Alice,30,Engineer = "Alice,30,Engineer"128name→ Alice, age→ 30, job→ Engineer = csv_lineAlice,30,Engineer.split(',')129print(f"CSV: name={nameAlice}, age={age30}, job={jobEngineer}")
    outputParsed '100,200': x=100, y=200
    CSV: name=Alice, age=30, job=Engineer
  1. point ← (3, 4), x ← 3, y ← 4, a ← 5, b ← 10, coordinates ← [100, 200, 300]

    1"""Basic unpacking examples"""23# Tuple unpacking4print("Tuple unpacking:")56point→ (3, 4) = (3, 4)7x→ 3, y→ 4 = point(3, 4)8print(f"point = {point(3, 4)}")9print(f"x = {x3}, y = {y4}")1011# Swap variables12a→ 5, b→ 10 = 5, 1013print(f"\nBefore swap: a={a5}, b={b10}")14a→ 10, b→ 5 = b, a15print(f"After swap: a={a10}, b={b5}")1617# List unpacking18print("\nList unpacking:")1920coordinates→ [100, 200, 300] = [100, 200, 300]21x→ 100, y→ 200, z→ 300 = coordinates[100, 200, 300]22print(f"coordinates = {coordinates[100, 200, 300]}")23print(f"x={x100}, y={y200}, z={z300}")2425# String unpacking26print("\nString unpacking:")2728name→ Alice = "Alice"29first→ A, second→ l, third→ i, fourth→ c, fifth→ e = nameAlice30print(f"name = '{nameAlice}'")31print(f"Letters: {firstA}, {secondl}, {thirdi}, {fourthc}, {fifthe}")3233# Multiple assignment34print("\nMultiple assignment:")3536# Single line assignment37a→ 1, b→ 2, c→ 3 = 1, 2, 338print(f"a={a1}, b={b2}, c={c3}")3940# From list41values→ [10, 20, 30] = [10, 20, 30]42x→ 10, y→ 20, z→ 30 = values[10, 20, 30]43print(f"x={x10}, y={y20}, z={z30}")4445# Function returns46print("\nFunction returns:")4748def get_user():49    return "Alice", 30, "alice@example.com"5051name, age, email = get_user()52print(f"User: {name}, {age}, {email}")
    outputTuple unpacking:
    point = (3, 4)
    x = 3, y = 4
    
    Before swap: a=5, b=10
    After swap: a=10, b=5
    
    List unpacking:
    coordinates = [100, 200, 300]
    x=100, y=200, z=300
    
    String unpacking:
    name = 'Alice'
    Letters: A, l, i, c, e
    
    Multiple assignment:
    a=1, b=2, c=3
    x=10, y=20, z=30
    
    Function returns:
  2. name ← Alice, age ← 30, email ← alice@example.com

    51name→ Alice, age→ 30, email→ alice@example.com = get_user()52print(f"User: {nameAlice}, {age30}, {emailalice@example.com}")5354def min_max(numbers):55    return min(numbers), max(numbers)5657minimum, maximum = min_max([5, 2, 8, 1, 9])58print(f"Min: {minimum}, Max: {maximum}")
    outputUser: Alice, 30, alice@example.com
  3. def min_max(numbers):

    54def min_max(numbers[5, 2, 8, 1, 9]):55    return min(numbers[5, 2, 8, 1, 9]), max(numbers)
  4. minimum ← 1, maximum ← 9, fruits ← ['apple', 'banana', 'cherry']

    57minimum→ 1, maximum→ 9 = min_max([5, 2, 8, 1, 9])58print(f"Min: {minimum1}, Max: {maximum9}")5960# Enumerate unpacking61print("\nEnumerate unpacking:")6263fruits→ ['apple', 'banana', 'cherry'] = ["apple", "banana", "cherry"]
    outputMin: 1, Max: 9
    
    Enumerate unpacking:
  5. for index, fruit in enumerate(fruits):

    pass 1 of 3
    65for index0, fruitapple in enumerate(fruits['apple', 'banana', 'cherry']):66    print(f"  {index0}: {fruitapple}")
    output  0: apple
    All 3 passes — pass 1 is the card above
    passindexfruit
    10apple
    21banana
    32cherry
  6. user ← {'name': 'Bob', 'age': 25, 'city': 'NYC'}

    68# Dict items unpacking69print("\nDict items unpacking:")7071user→ {'name': 'Bob', 'age': 25, 'city': 'NYC'} = {"name": "Bob", "age": 25, "city": "NYC"}
    output
    Dict items unpacking:
  7. for key, value in user.items():

    pass 1 of 3
    73for keyname, valueBob in user{'name': 'Bob', 'age': 25, 'city': 'NYC'}.items():74    print(f"  {keyname}: {valueBob}")
    output  name: Bob
    All 3 passes — pass 1 is the card above
    passkeyvalue
    1nameBob
    2age25
    3cityNYC
  8. names ← ['Alice', 'Bob', 'Charlie'], scores ← [85, 92, 78]

    76# Zip unpacking77print("\nZip unpacking:")7879names→ ['Alice', 'Bob', 'Charlie'] = ["Alice", "Bob", "Charlie"]80scores→ [85, 92, 78] = [85, 92, 78]
    output
    Zip unpacking:
  9. for name, score in zip(names, scores):

    pass 1 of 3
    82for nameAlice, score85 in zip(names['Alice', 'Bob', 'Charlie'], scores[85, 92, 78]):83    print(f"  {nameAlice}: {score85}")
    output  Alice: 85
    All 3 passes — pass 1 is the card above
    passnamescore
    1Alice85
    2Bob92
    3Charlie78
  10. data ← ('Alice', (30, 'alice@example.com')), name ← Alice, age ← 30

    85# Nested tuples86print("\nNested tuples:")8788data→ ('Alice', (30, 'alice@example.com')) = ("Alice", (30, "alice@example.com"))89name→ Alice, (age→ 30, email→ alice@example.com) = data('Alice', (30, 'alice@example.com'))90print(f"name={nameAlice}, age={age30}, email={emailalice@example.com}")9192# Error handling93print("\nError handling:")
    output
    Nested tuples:
    name=Alice, age=30, email=alice@example.com
    
    Error handling:
  11. except ValueError as e:

    96    a, b = [1, 2, 3]  # Too many values97except ValueError as e:98    print(f"Error: {etoo many values to unpack (expected 2)}")
    outputError: too many values to unpack (expected 2)
  12. except ValueError as e:

    101    a, b, c = [1, 2]  # Too few values102except ValueError as e:103    print(f"Error: {enot enough values to unpack (expected 3, got 2)}")
    outputError: not enough values to unpack (expected 3, got 2)
  13. first ← 1, _ ← 2, third ← 3, name ← Alice, city ← NYC, coord_str ← 100,200

    105# Underscore for ignored values106print("\nUnderscore for ignored values:")107108# Ignore some values109first→ 1, _→ 2, third→ 3 = [1, 2, 3]110print(f"first={first1}, third={third3} (ignored middle)")111112name→ Alice, _→ alice@example.com, _, city→ NYC = ["Alice", 30, "alice@example.com", "NYC"]113print(f"name={nameAlice}, city={cityNYC} (ignored age and email)")114115# Practical example116print("\nPractical example:")117118def parse_coordinate(coord_str):119    """Parse 'x,y' string into tuple"""120    return tuple(map(int, coord_str.split(',')))121122coord_str→ 100,200 = "100,200"123x, y = parse_coordinate(coord_str100,200)124print(f"Parsed '{coord_str}': x={x}, y={y}")
    output
    Underscore for ignored values:
    first=1, third=3 (ignored middle)
    name=Alice, city=NYC (ignored age and email)
    
    Practical example:
  14. def parse_coordinate(coord_str):

    118def parse_coordinate(coord_str100,200):119    """Parse 'x,y' string into tuple"""120    return tuple(map(int, coord_str100,200.split(',')))
  15. x ← 100, y ← 200, csv_line ← Alice,30,Engineer, name ← Alice, age ← 30

    122coord_str = "100,200"123x→ 100, y→ 200 = parse_coordinate(coord_str100,200)124print(f"Parsed '{coord_str100,200}': x={x100}, y={y200}")125126# Process CSV line127csv_line→ Alice,30,Engineer = "Alice,30,Engineer"128name→ Alice, age→ 30, job→ Engineer = csv_lineAlice,30,Engineer.split(',')129print(f"CSV: name={nameAlice}, age={age30}, job={jobEngineer}")
    outputParsed '100,200': x=100, y=200
    CSV: name=Alice, age=30, job=Engineer
  1. point ← (100, 200), x ← 100, y ← 200, a ← 5, b ← 10, coordinates ← [100, 200, 300]

    1"""Basic unpacking examples"""23# Tuple unpacking4print("Tuple unpacking:")56point→ (100, 200) = (100, 200)7x→ 100, y→ 200 = point(100, 200)8print(f"point = {point(100, 200)}")9print(f"x = {x100}, y = {y200}")1011# Swap variables12a→ 5, b→ 10 = 5, 1013print(f"\nBefore swap: a={a5}, b={b10}")14a→ 10, b→ 5 = b, a15print(f"After swap: a={a10}, b={b5}")1617# List unpacking18print("\nList unpacking:")1920coordinates→ [100, 200, 300] = [100, 200, 300]21x→ 100, y→ 200, z→ 300 = coordinates[100, 200, 300]22print(f"coordinates = {coordinates[100, 200, 300]}")23print(f"x={x100}, y={y200}, z={z300}")2425# String unpacking26print("\nString unpacking:")2728name→ Alice = "Alice"29first→ A, second→ l, third→ i, fourth→ c, fifth→ e = nameAlice30print(f"name = '{nameAlice}'")31print(f"Letters: {firstA}, {secondl}, {thirdi}, {fourthc}, {fifthe}")3233# Multiple assignment34print("\nMultiple assignment:")3536# Single line assignment37a→ 1, b→ 2, c→ 3 = 1, 2, 338print(f"a={a1}, b={b2}, c={c3}")3940# From list41values→ [10, 20, 30] = [10, 20, 30]42x→ 10, y→ 20, z→ 30 = values[10, 20, 30]43print(f"x={x10}, y={y20}, z={z30}")4445# Function returns46print("\nFunction returns:")4748def get_user():49    return "Alice", 30, "alice@example.com"5051name, age, email = get_user()52print(f"User: {name}, {age}, {email}")
    outputTuple unpacking:
    point = (100, 200)
    x = 100, y = 200
    
    Before swap: a=5, b=10
    After swap: a=10, b=5
    
    List unpacking:
    coordinates = [100, 200, 300]
    x=100, y=200, z=300
    
    String unpacking:
    name = 'Alice'
    Letters: A, l, i, c, e
    
    Multiple assignment:
    a=1, b=2, c=3
    x=10, y=20, z=30
    
    Function returns:
  2. name ← Alice, age ← 30, email ← alice@example.com

    51name→ Alice, age→ 30, email→ alice@example.com = get_user()52print(f"User: {nameAlice}, {age30}, {emailalice@example.com}")5354def min_max(numbers):55    return min(numbers), max(numbers)5657minimum, maximum = min_max([5, 2, 8, 1, 9])58print(f"Min: {minimum}, Max: {maximum}")
    outputUser: Alice, 30, alice@example.com
  3. def min_max(numbers):

    54def min_max(numbers[5, 2, 8, 1, 9]):55    return min(numbers[5, 2, 8, 1, 9]), max(numbers)
  4. minimum ← 1, maximum ← 9, fruits ← ['apple', 'banana', 'cherry']

    57minimum→ 1, maximum→ 9 = min_max([5, 2, 8, 1, 9])58print(f"Min: {minimum1}, Max: {maximum9}")5960# Enumerate unpacking61print("\nEnumerate unpacking:")6263fruits→ ['apple', 'banana', 'cherry'] = ["apple", "banana", "cherry"]
    outputMin: 1, Max: 9
    
    Enumerate unpacking:
  5. for index, fruit in enumerate(fruits):

    pass 1 of 3
    65for index0, fruitapple in enumerate(fruits['apple', 'banana', 'cherry']):66    print(f"  {index0}: {fruitapple}")
    output  0: apple
    All 3 passes — pass 1 is the card above
    passindexfruit
    10apple
    21banana
    32cherry
  6. user ← {'name': 'Bob', 'age': 25, 'city': 'NYC'}

    68# Dict items unpacking69print("\nDict items unpacking:")7071user→ {'name': 'Bob', 'age': 25, 'city': 'NYC'} = {"name": "Bob", "age": 25, "city": "NYC"}
    output
    Dict items unpacking:
  7. for key, value in user.items():

    pass 1 of 3
    73for keyname, valueBob in user{'name': 'Bob', 'age': 25, 'city': 'NYC'}.items():74    print(f"  {keyname}: {valueBob}")
    output  name: Bob
    All 3 passes — pass 1 is the card above
    passkeyvalue
    1nameBob
    2age25
    3cityNYC
  8. names ← ['Alice', 'Bob', 'Charlie'], scores ← [85, 92, 78]

    76# Zip unpacking77print("\nZip unpacking:")7879names→ ['Alice', 'Bob', 'Charlie'] = ["Alice", "Bob", "Charlie"]80scores→ [85, 92, 78] = [85, 92, 78]
    output
    Zip unpacking:
  9. for name, score in zip(names, scores):

    pass 1 of 3
    82for nameAlice, score85 in zip(names['Alice', 'Bob', 'Charlie'], scores[85, 92, 78]):83    print(f"  {nameAlice}: {score85}")
    output  Alice: 85
    All 3 passes — pass 1 is the card above
    passnamescore
    1Alice85
    2Bob92
    3Charlie78
  10. data ← ('Alice', (30, 'alice@example.com')), name ← Alice, age ← 30

    85# Nested tuples86print("\nNested tuples:")8788data→ ('Alice', (30, 'alice@example.com')) = ("Alice", (30, "alice@example.com"))89name→ Alice, (age→ 30, email→ alice@example.com) = data('Alice', (30, 'alice@example.com'))90print(f"name={nameAlice}, age={age30}, email={emailalice@example.com}")9192# Error handling93print("\nError handling:")
    output
    Nested tuples:
    name=Alice, age=30, email=alice@example.com
    
    Error handling:
  11. except ValueError as e:

    96    a, b = [1, 2, 3]  # Too many values97except ValueError as e:98    print(f"Error: {etoo many values to unpack (expected 2)}")
    outputError: too many values to unpack (expected 2)
  12. except ValueError as e:

    101    a, b, c = [1, 2]  # Too few values102except ValueError as e:103    print(f"Error: {enot enough values to unpack (expected 3, got 2)}")
    outputError: not enough values to unpack (expected 3, got 2)
  13. first ← 1, _ ← 2, third ← 3, name ← Alice, city ← NYC, coord_str ← 100,200

    105# Underscore for ignored values106print("\nUnderscore for ignored values:")107108# Ignore some values109first→ 1, _→ 2, third→ 3 = [1, 2, 3]110print(f"first={first1}, third={third3} (ignored middle)")111112name→ Alice, _→ alice@example.com, _, city→ NYC = ["Alice", 30, "alice@example.com", "NYC"]113print(f"name={nameAlice}, city={cityNYC} (ignored age and email)")114115# Practical example116print("\nPractical example:")117118def parse_coordinate(coord_str):119    """Parse 'x,y' string into tuple"""120    return tuple(map(int, coord_str.split(',')))121122coord_str→ 100,200 = "100,200"123x, y = parse_coordinate(coord_str100,200)124print(f"Parsed '{coord_str}': x={x}, y={y}")
    output
    Underscore for ignored values:
    first=1, third=3 (ignored middle)
    name=Alice, city=NYC (ignored age and email)
    
    Practical example:
  14. def parse_coordinate(coord_str):

    118def parse_coordinate(coord_str100,200):119    """Parse 'x,y' string into tuple"""120    return tuple(map(int, coord_str100,200.split(',')))
  15. x ← 100, y ← 200, csv_line ← Alice,30,Engineer, name ← Alice, age ← 30

    122coord_str = "100,200"123x→ 100, y→ 200 = parse_coordinate(coord_str100,200)124print(f"Parsed '{coord_str100,200}': x={x100}, y={y200}")125126# Process CSV line127csv_line→ Alice,30,Engineer = "Alice,30,Engineer"128name→ Alice, age→ 30, job→ Engineer = csv_lineAlice,30,Engineer.split(',')129print(f"CSV: name={nameAlice}, age={age30}, job={jobEngineer}")
    outputParsed '100,200': x=100, y=200
    CSV: name=Alice, age=30, job=Engineer

Basic unpacking matches the number of variables to the number of elements in the sequence. It works with tuples, lists, strings, and any iterable.

unpacking Extracting values from a sequence into individual variables using assignment syntax like `a, b = [1, 2]`.

Extended Unpacking with *

star_unpacking.py
Replay: real traced execution (multi-file project)
"""Extended unpacking with * operator"""

# Basic * unpacking
print("Basic * unpacking:")

numbers = [1, 2, 3, 4, 5]

# First and rest
first, *rest = numbers
print(f"numbers = {numbers}")
print(f"first = {first}")
print(f"rest = {rest}")

# Last and rest
print("\nLast and rest:")

*beginning, last = numbers
print(f"beginning = {beginning}")
print(f"last = {last}")

# Middle extraction
print("\nMiddle extraction:")

first, *middle, last = numbers
print(f"first = {first}")
print(f"middle = {middle}")
print(f"last = {last}")

# Multiple *rest patterns
print("\nMultiple patterns:")

# First two and rest
a, b, *rest = [1, 2, 3, 4, 5]
print(f"a={a}, b={b}, rest={rest}")

# First, middle, last two
first, *middle, second_last, last = [1, 2, 3, 4, 5, 6]
print(f"first={first}, middle={middle}, second_last={second_last}, last={last}")

# Empty rest
print("\nEmpty rest:")

# Rest can be empty
a, b, *rest = [1, 2]
print(f"a={a}, b={b}, rest={rest} (empty list)")

first, *middle, last = [1, 2]
print(f"first={first}, middle={middle} (empty), last={last}")

# String unpacking
print("\nString unpacking:")

text = "Python"
first, *middle, last = text
print(f"text = '{text}'")
print(f"first = '{first}', middle = {middle}, last = '{last}'")

# Function arguments
print("\nFunction arguments:")

def process(first, *rest):
    print(f"  First: {first}")
    print(f"  Rest: {rest}")

process(1, 2, 3, 4, 5)

# Splitting data
print("\nSplitting data:")

# CSV line
csv = "Alice,30,Engineer,NYC,USA"
name, age, *location = csv.split(',')
print(f"name={name}, age={age}, location={location}")

# Log parsing
log = "2024-01-15 10:30:45 ERROR Database connection failed"
date, time, level, *message = log.split()
print(f"level={level}, message={' '.join(message)}")

# Head and tail
print("\nHead and tail:")

def head_tail(items):
    """Get first element and rest"""
    if not items:
        return None, []
    head, *tail = items
    return head, tail

numbers = [10, 20, 30, 40]
head, tail = head_tail(numbers)
print(f"head={head}, tail={tail}")

# Process all but first
print("\nProcess all but first:")

# Skip header
data = ["Name,Age,City", "Alice,30,NYC", "Bob,25,LA"]
header, *rows = data

print(f"Header: {header}")
print("Rows:")
for row in rows:
    print(f"  {row}")

# Nested unpacking
print("\nNested unpacking:")

data = [1, [2, 3, 4], 5]
first, [second, *middle], last = data
print(f"first={first}, second={second}, middle={middle}, last={last}")

# Split into halves
print("\nSplit into halves:")

# Only ONE * allowed per unpacking
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
mid = len(numbers) // 2
first_half = numbers[:mid]
second_half = numbers[mid:]
print(f"first_half={first_half}")
print(f"second_half={second_half}")

# Practical examples
print("\nPractical examples:")

# Parse command with arguments
command = "git commit -m 'Initial commit' --author 'Alice'"
cmd, *args = command.split()
print(f"Command: {cmd}")
print(f"Arguments: {args}")

# Process scores, ignore outliers
scores = [95, 92, 88, 5, 90, 87, 100]  # 5 is outlier
sorted_scores = sorted(scores)
lowest, *middle_scores, highest = sorted_scores
print(f"Outliers: {lowest}, {highest}")
print(f"Valid scores: {middle_scores}")

# Unpack with default
def get_values():
    return [1]  # Only one value

first, *rest = get_values()
rest_with_default = rest if rest else [0]
print(f"first={first}, rest with default={rest_with_default}")

  1. numbers ← [1, 2, 3, 4, 5], first ← 1, rest ← [2, 3, 4, 5], beginning ← [1, 2, 3, 4]

    1"""Extended unpacking with * operator"""23# Basic * unpacking4print("Basic * unpacking:")56numbers→ [1, 2, 3, 4, 5] = [1, 2, 3, 4, 5]78# First and rest9first→ 1, *rest→ [2, 3, 4, 5] = numbers[1, 2, 3, 4, 5]10print(f"numbers = {numbers[1, 2, 3, 4, 5]}")11print(f"first = {first1}")12print(f"rest = {rest[2, 3, 4, 5]}")1314# Last and rest15print("\nLast and rest:")1617*beginning→ [1, 2, 3, 4], last→ 5 = numbers[1, 2, 3, 4, 5]18print(f"beginning = {beginning[1, 2, 3, 4]}")19print(f"last = {last5}")2021# Middle extraction22print("\nMiddle extraction:")2324first→ 1, *middle→ [2, 3, 4], last→ 5 = numbers[1, 2, 3, 4, 5]25print(f"first = {first1}")26print(f"middle = {middle[2, 3, 4]}")27print(f"last = {last5}")2829# Multiple *rest patterns30print("\nMultiple patterns:")3132# First two and rest33a→ 1, b→ 2, *rest→ [3, 4, 5] = [1, 2, 3, 4, 5]34print(f"a={a1}, b={b2}, rest={rest[3, 4, 5]}")3536# First, middle, last two37first→ 1, *middle→ [2, 3, 4], second_last→ 5, last→ 6 = [1, 2, 3, 4, 5, 6]38print(f"first={first1}, middle={middle[2, 3, 4]}, second_last={second_last5}, last={last6}")3940# Empty rest41print("\nEmpty rest:")4243# Rest can be empty44a→ 1, b→ 2, *rest→ [] = [1, 2]45print(f"a={a1}, b={b2}, rest={rest[]} (empty list)")4647first→ 1, *middle→ [], last→ 2 = [1, 2]48print(f"first={first1}, middle={middle[]} (empty), last={last2}")4950# String unpacking51print("\nString unpacking:")5253text→ Python = "Python"54first→ P, *middle→ ['y', 't', 'h', 'o'], last→ n = textPython55print(f"text = '{textPython}'")56print(f"first = '{firstP}', middle = {middle['y', 't', 'h', 'o']}, last = '{lastn}'")5758# Function arguments59print("\nFunction arguments:")6061def process(first, *rest):62    print(f"  First: {first}")63    print(f"  Rest: {rest}")6465process(1, 2, 3, 4, 5)
    outputBasic * unpacking:
    numbers = [1, 2, 3, 4, 5]
    first = 1
    rest = [2, 3, 4, 5]
    
    Last and rest:
    beginning = [1, 2, 3, 4]
    last = 5
    
    Middle extraction:
    first = 1
    middle = [2, 3, 4]
    last = 5
    
    Multiple patterns:
    a=1, b=2, rest=[3, 4, 5]
    first=1, middle=[2, 3, 4], second_last=5, last=6
    
    Empty rest:
    a=1, b=2, rest=[] (empty list)
    first=1, middle=[] (empty), last=2
    
    String unpacking:
    text = 'Python'
    first = 'P', middle = ['y', 't', 'h', 'o'], last = 'n'
    
    Function arguments:
  2. def process(first, *rest):

    61def process(first1, *rest(2, 3, 4, 5)):62    print(f"  First: {first1}")63    print(f"  Rest: {rest(2, 3, 4, 5)}")
    output  First: 1
      Rest: (2, 3, 4, 5)
  3. csv ← Alice,30,Engineer,NYC,USA, name ← Alice, age ← 30, location ← ['Engineer', 'NYC', 'USA']

    65process(1, 2, 3, 4, 5)6667# Splitting data68print("\nSplitting data:")6970# CSV line71csv→ Alice,30,Engineer,NYC,USA = "Alice,30,Engineer,NYC,USA"72name→ Alice, age→ 30, *location→ ['Engineer', 'NYC', 'USA'] = csvAlice,30,Engineer,NYC,USA.split(',')73print(f"name={nameAlice}, age={age30}, location={location['Engineer', 'NYC', 'USA']}")7475# Log parsing76log→ 2024-01-15 10:30:45 ERROR Database connection failed = "2024-01-15 10:30:45 ERROR Database connection failed"77date→ 2024-01-15, time→ 10:30:45, level→ ERROR, *message→ ['Database', 'connection', 'failed'] = log2024-01-15 10:30:45 ERROR Database connection failed.split()78print(f"level={levelERROR}, message={' '.join(message['Database', 'connection', 'failed'])}")7980# Head and tail81print("\nHead and tail:")8283def head_tail(items):84    """Get first element and rest"""85    if not items:86        return None, []87    head, *tail = items88    return head, tail8990numbers→ [10, 20, 30, 40] = [10, 20, 30, 40]91head, tail = head_tail(numbers[10, 20, 30, 40])92print(f"head={head}, tail={tail}")
    output
    Splitting data:
    name=Alice, age=30, location=['Engineer', 'NYC', 'USA']
    level=ERROR, message=Database connection failed
    
    Head and tail:
  4. head ← 10, tail ← [20, 30, 40]

    83def head_tail(items[10, 20, 30, 40]):84    """Get first element and rest"""85    if not items:86        return None, []87    head→ 10, *tail→ [20, 30, 40] = items[10, 20, 30, 40]88    return head10, tail[20, 30, 40]
  5. head ← 10, tail ← [20, 30, 40], data ← ['Name,Age,City', 'Alice,30,NYC', 'Bob,25,LA']

    90numbers = [10, 20, 30, 40]91head→ 10, tail→ [20, 30, 40] = head_tail(numbers[10, 20, 30, 40])92print(f"head={head10}, tail={tail[20, 30, 40]}")9394# Process all but first95print("\nProcess all but first:")9697# Skip header98data→ ['Name,Age,City', 'Alice,30,NYC', 'Bob,25,LA'] = ["Name,Age,City", "Alice,30,NYC", "Bob,25,LA"]99header→ Name,Age,City, *rows→ ['Alice,30,NYC', 'Bob,25,LA'] = data['Name,Age,City', 'Alice,30,NYC', 'Bob,25,LA']100101print(f"Header: {headerName,Age,City}")102print("Rows:")103for row in rows:
    outputhead=10, tail=[20, 30, 40]
    
    Process all but first:
    Header: Name,Age,City
    Rows:
  6. for row in rows:

    pass 1 of 2
    102print("Rows:")103for rowAlice,30,NYC in rows['Alice,30,NYC', 'Bob,25,LA']:104    print(f"  {rowAlice,30,NYC}")
    output  Alice,30,NYC
  7. for row in rows:

    pass 2 of 2
    102print("Rows:")103for rowBob,25,LA in rows['Alice,30,NYC', 'Bob,25,LA']:104    print(f"  {rowBob,25,LA}")
    output  Bob,25,LA
  8. data ← [1, [2, 3, 4], 5], first ← 1, second ← 2, middle ← [3, 4]

    106# Nested unpacking107print("\nNested unpacking:")108109data→ [1, [2, 3, 4], 5] = [1, [2, 3, 4], 5]110first→ 1, [second→ 2, *middle→ [3, 4]], last→ 5 = data[1, [2, 3, 4], 5]111print(f"first={first1}, second={second2}, middle={middle[3, 4]}, last={last5}")112113# Split into halves114print("\nSplit into halves:")115116# Only ONE * allowed per unpacking117numbers→ [1, 2, 3, 4, 5, 6, 7, 8, 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9]118mid→ 4 = len(numbers[1, 2, 3, 4, 5, 6, 7, 8, 9]) // 2119first_half→ [1, 2, 3, 4] = numbers[:mid][1, 2, 3, 4]120second_half→ [5, 6, 7, 8, 9] = numbers[mid:][5, 6, 7, 8, 9]121print(f"first_half={first_half[1, 2, 3, 4]}")122print(f"second_half={second_half[5, 6, 7, 8, 9]}")123124# Practical examples125print("\nPractical examples:")126127# Parse command with arguments128command→ git commit -m 'Initial commit' --author 'Alice' = "git commit -m 'Initial commit' --author 'Alice'"129cmd→ git, *args→ ['commit', '-m', "'Initial", "commit'", '--author', "'Alice'"] = commandgit commit -m 'Initial commit' --author 'Alice'.split()130print(f"Command: {cmdgit}")131print(f"Arguments: {args['commit', '-m', "'Initial", "commit'", '--author', "'Alice'"]}")132133# Process scores, ignore outliers134scores→ [95, 92, 88, 5, 90, 87, 100] = [95, 92, 88, 5, 90, 87, 100]  # 5 is outlier135sorted_scores→ [5, 87, 88, 90, 92, 95, 100] = sorted(scores[95, 92, 88, 5, 90, 87, 100])136lowest→ 5, *middle_scores→ [87, 88, 90, 92, 95], highest→ 100 = sorted_scores[5, 87, 88, 90, 92, 95, 100]137print(f"Outliers: {lowest5}, {highest100}")138print(f"Valid scores: {middle_scores[87, 88, 90, 92, 95]}")139140# Unpack with default141def get_values():142    return [1]  # Only one value143144first, *rest = get_values()145rest_with_default = rest if rest else [0]
    output
    Nested unpacking:
    first=1, second=2, middle=[3, 4], last=5
    
    Split into halves:
    first_half=[1, 2, 3, 4]
    second_half=[5, 6, 7, 8, 9]
    
    Practical examples:
    Command: git
    Arguments: ['commit', '-m', "'Initial", "commit'", '--author', "'Alice'"]
    Outliers: 5, 100
    Valid scores: [87, 88, 90, 92, 95]
  9. first ← 1, rest ← [], rest_with_default ← [0]

    144first→ 1, *rest→ [] = get_values()145rest_with_default→ [0] = rest[] if rest else [0]146print(f"first={first1}, rest with default={rest_with_default[0]}")
    outputfirst=1, rest with default=[0]

The * operator can appear at the beginning, middle, or end of the unpacking pattern to capture the "rest" of the elements.

star unpacking Using `*variable` to capture multiple remaining elements into a list, enabling flexible extraction from variable-length sequences.

Nested Unpacking

nested_unpacking.py
Replay: real traced execution (multi-file project)
"""Nested unpacking examples"""

# Nested tuples
print("Nested tuples:")

person = ("Alice", (30, "alice@example.com"))
name, (age, email) = person
print(f"person = {person}")
print(f"name={name}, age={age}, email={email}")

# Nested lists
print("\nNested lists:")

data = [[1, 2], [3, 4], [5, 6]]
[a, b], [c, d], [e, f] = data
print(f"data = {data}")
print(f"a={a}, b={b}, c={c}, d={d}, e={e}, f={f}")

# Mixed nesting
print("\nMixed nesting:")

record = ("Bob", [25, "NYC"], ("Engineer", 75000))
name, [age, city], (job, salary) = record
print(f"name={name}, age={age}, city={city}, job={job}, salary={salary}")

# Partial nested unpacking
print("\nPartial nested unpacking:")

data = ("Alice", (30, "alice@example.com", "NYC"))
name, info = data
age, email, city = info
print(f"name={name}")
print(f"info={info}")
print(f"age={age}, email={email}, city={city}")

# Nested with *
print("\nNested with *:")

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[first, *rest], middle, last = matrix
print(f"first={first}, rest={rest}")
print(f"middle={middle}")
print(f"last={last}")

# Nested star unpacking
data = [1, [2, 3, 4, 5], 6]
first, [second, *middle, last_inner], last = data
print(f"first={first}, second={second}, middle={middle}, last_inner={last_inner}, last={last}")

# Coordinate pairs
print("\nCoordinate pairs:")

points = [(10, 20), (30, 40), (50, 60)]

for x, y in points:
    print(f"  Point: ({x}, {y})")

# 3D points
points_3d = [(1, 2, 3), (4, 5, 6)]
for x, y, z in points_3d:
    print(f"  3D Point: ({x}, {y}, {z})")

# Nested dict items
print("\nNested dict items:")

users = {
    "user1": {"name": "Alice", "age": 30},
    "user2": {"name": "Bob", "age": 25}
}

for user_id, user_info in users.items():
    name = user_info["name"]
    age = user_info["age"]
    print(f"  {user_id}: {name}, {age}")

# Function returns
print("\nFunction returns:")

def get_stats(numbers):
    return min(numbers), (sum(numbers), len(numbers)), max(numbers)

minimum, (total, count), maximum = get_stats([1, 2, 3, 4, 5])
print(f"min={minimum}, total={total}, count={count}, max={maximum}")
print(f"average={total/count}")

# Nested enumerate
print("\nNested enumerate:")

matrix = [[1, 2], [3, 4], [5, 6]]

for i, (a, b) in enumerate(matrix):
    print(f"  Row {i}: a={a}, b={b}")

# JSON-like structure
print("\nJSON-like structure:")

response = {
    "status": 200,
    "data": {
        "user": {"name": "Alice", "id": 123},
        "items": [1, 2, 3]
    }
}

# Extract nested values
status = response["status"]
user_data = response["data"]["user"]
name, user_id = user_data["name"], user_data["id"]

print(f"status={status}, user={name} (id={user_id})")

# Complex nesting
print("\nComplex nesting:")

# Table data
table = [
    ["Name", "Scores"],
    ["Alice", [85, 90, 92]],
    ["Bob", [78, 88, 85]]
]

header, *rows = table
[name_col, scores_col] = header

print(f"Columns: {name_col}, {scores_col}")
print("Data:")
for name, scores in rows:
    first, *rest = scores
    print(f"  {name}: first={first}, rest={rest}")

# Tree structure
print("\nTree structure:")

# Binary tree node: (value, left, right)
tree = (5, (3, None, None), (7, (6, None, None), (9, None, None)))

value, left, right = tree
print(f"Root: {value}")

if left:
    left_val, _, _ = left
    print(f"Left child: {left_val}")

if right:
    right_val, right_left, right_right = right
    print(f"Right child: {right_val}")
    if right_left:
        print(f"Right-left grandchild: {right_left[0]}")

# Practical example
print("\nPractical example:")

# Parse nested CSV
csv_data = [
    ("Alice", "30", "Engineer,Senior,NYC"),
    ("Bob", "25", "Designer,Junior,LA")
]

for name, age, job_info in csv_data:
    job, level, city = job_info.split(',')
    print(f"  {name} ({age}): {level} {job} in {city}")

# Nested coordinates
regions = [
    ("North", [(0, 0), (10, 0), (10, 10), (0, 10)]),
    ("South", [(0, -10), (10, -10), (10, 0), (0, 0)])
]

for region_name, corners in regions:
    (x1, y1), (x2, y2), (x3, y3), (x4, y4) = corners
    print(f"  {region_name}: corners at ({x1},{y1}), ({x2},{y2}), ({x3},{y3}), ({x4},{y4})")

  1. person ← ('Alice', (30, 'alice@example.com')), name ← Alice, age ← 30

    1"""Nested unpacking examples"""23# Nested tuples4print("Nested tuples:")56person→ ('Alice', (30, 'alice@example.com')) = ("Alice", (30, "alice@example.com"))7name→ Alice, (age→ 30, email→ alice@example.com) = person('Alice', (30, 'alice@example.com'))8print(f"person = {person('Alice', (30, 'alice@example.com'))}")9print(f"name={nameAlice}, age={age30}, email={emailalice@example.com}")1011# Nested lists12print("\nNested lists:")1314data→ [[1, 2], [3, 4], [5, 6]] = [[1, 2], [3, 4], [5, 6]]15[a→ 1, b→ 2], [c→ 3, d→ 4], [e→ 5, f→ 6] = data[[1, 2], [3, 4], [5, 6]]16print(f"data = {data[[1, 2], [3, 4], [5, 6]]}")17print(f"a={a1}, b={b2}, c={c3}, d={d4}, e={e5}, f={f6}")1819# Mixed nesting20print("\nMixed nesting:")2122record = ("Bob", [25, "NYC"], ("Engineer", 75000))23name→ Bob, [age→ 25, city→ NYC], (job→ Engineer, salary→ 75000) = record('Bob', [25, 'NYC'], ('Engineer', 75000))24print(f"name={nameBob}, age={age25}, city={cityNYC}, job={jobEngineer}, salary={salary75000}")2526# Partial nested unpacking27print("\nPartial nested unpacking:")2829data→ ('Alice', (30, 'alice@example.com', 'NYC')) = ("Alice", (30, "alice@example.com", "NYC"))30name→ Alice, info→ (30, 'alice@example.com', 'NYC') = data('Alice', (30, 'alice@example.com', 'NYC'))31age→ 30, email→ alice@example.com, city→ NYC = info(30, 'alice@example.com', 'NYC')32print(f"name={nameAlice}")33print(f"info={info(30, 'alice@example.com', 'NYC')}")34print(f"age={age30}, email={emailalice@example.com}, city={cityNYC}")3536# Nested with *37print("\nNested with *:")3839matrix→ [[1, 2, 3], [4, 5, 6], [7, 8, 9]] = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]40[first→ 1, *rest→ [2, 3]], middle→ [4, 5, 6], last→ [7, 8, 9] = matrix[[1, 2, 3], [4, 5, 6], [7, 8, 9]]41print(f"first={first1}, rest={rest[2, 3]}")42print(f"middle={middle[4, 5, 6]}")43print(f"last={last[7, 8, 9]}")4445# Nested star unpacking46data→ [1, [2, 3, 4, 5], 6] = [1, [2, 3, 4, 5], 6]47first→ 1, [second→ 2, *middle→ [3, 4], last_inner→ 5], last→ 6 = data[1, [2, 3, 4, 5], 6]48print(f"first={first1}, second={second2}, middle={middle[3, 4]}, last_inner={last_inner5}, last={last6}")4950# Coordinate pairs51print("\nCoordinate pairs:")5253points→ [(10, 20), (30, 40), (50, 60)] = [(10, 20), (30, 40), (50, 60)]
    outputNested tuples:
    person = ('Alice', (30, 'alice@example.com'))
    name=Alice, age=30, email=alice@example.com
    
    Nested lists:
    data = [[1, 2], [3, 4], [5, 6]]
    a=1, b=2, c=3, d=4, e=5, f=6
    
    Mixed nesting:
    name=Bob, age=25, city=NYC, job=Engineer, salary=75000
    
    Partial nested unpacking:
    name=Alice
    info=(30, 'alice@example.com', 'NYC')
    age=30, email=alice@example.com, city=NYC
    
    Nested with *:
    first=1, rest=[2, 3]
    middle=[4, 5, 6]
    last=[7, 8, 9]
    first=1, second=2, middle=[3, 4], last_inner=5, last=6
    
    Coordinate pairs:
  2. for x, y in points:

    pass 1 of 3
    55for x10, y20 in points[(10, 20), (30, 40), (50, 60)]:56    print(f"  Point: ({x10}, {y20})")
    output  Point: (10, 20)
    All 3 passes — pass 1 is the card above
    passxy
    11020
    23040
    35060
  3. points_3d ← [(1, 2, 3), (4, 5, 6)]

    58# 3D points59points_3d→ [(1, 2, 3), (4, 5, 6)] = [(1, 2, 3), (4, 5, 6)]60for x, y, z in points_3d:
  4. for x, y, z in points_3d:

    pass 1 of 2
    59points_3d = [(1, 2, 3), (4, 5, 6)]60for x1, y2, z3 in points_3d[(1, 2, 3), (4, 5, 6)]:61    print(f"  3D Point: ({x1}, {y2}, {z3})")
    output  3D Point: (1, 2, 3)
  5. for x, y, z in points_3d:

    pass 2 of 2
    59points_3d = [(1, 2, 3), (4, 5, 6)]60for x4, y5, z6 in points_3d[(1, 2, 3), (4, 5, 6)]:61    print(f"  3D Point: ({x4}, {y5}, {z6})")
    output  3D Point: (4, 5, 6)
  6. users ← {'user1': {'name': 'Alice', 'age': 30}, 'user2': {'name': 'Bob', 'age': 25}}

    63# Nested dict items64print("\nNested dict items:")6566users→ {'user1': {'name': 'Alice', 'age': 30}, 'user2': {'name': 'Bob', 'age': 25}} = {67    "user1": {"name": "Alice", "age": 30},68    "user2": {"name": "Bob", "age": 25}69}
    output
    Nested dict items:
  7. name ← Alice, age ← 30

    pass 1 of 2
    71for user_iduser1, user_info{'name': 'Alice', 'age': 30} in users{'user1': {'name': 'Alice', 'age': 30}, 'user2': {'name': 'Bob', 'age': 25}}.items():72    name→ Alice = user_info["name"]Alice73    age→ 30 = user_info["age"]3074    print(f"  {user_iduser1}: {nameAlice}, {age30}")
    output  user1: Alice, 30
  8. name ← Bob, age ← 25

    pass 2 of 2
    71for user_iduser2, user_info{'name': 'Bob', 'age': 25} in users{'user1': {'name': 'Alice', 'age': 30}, 'user2': {'name': 'Bob', 'age': 25}}.items():72    name→ Bob = user_info["name"]Bob73    age→ 25 = user_info["age"]2574    print(f"  {user_iduser2}: {nameBob}, {age25}")
    output  user2: Bob, 25
  9. print(" Function returns:")

    76# Function returns77print("\nFunction returns:")7879def get_stats(numbers):80    return min(numbers), (sum(numbers), len(numbers)), max(numbers)8182minimum, (total, count), maximum = get_stats([1, 2, 3, 4, 5])83print(f"min={minimum}, total={total}, count={count}, max={maximum}")
    output
    Function returns:
  10. def get_stats(numbers):

    79def get_stats(numbers[1, 2, 3, 4, 5]):80    return min(numbers[1, 2, 3, 4, 5]), (sum(numbers), len(numbers)), max(numbers)
  11. minimum ← 1, total ← 15, count ← 5, maximum ← 5, matrix ← [[1, 2], [3, 4], [5, 6]]

    82minimum→ 1, (total→ 15, count→ 5), maximum→ 5 = get_stats([1, 2, 3, 4, 5])83print(f"min={minimum1}, total={total15}, count={count5}, max={maximum5}")84print(f"average={total15/count5}")8586# Nested enumerate87print("\nNested enumerate:")8889matrix→ [[1, 2], [3, 4], [5, 6]] = [[1, 2], [3, 4], [5, 6]]
    outputmin=1, total=15, count=5, max=5
    average=3.0
    
    Nested enumerate:
  12. for i, (a, b) in enumerate(matrix):

    pass 1 of 3
    91for i0, (a1, b2) in enumerate(matrix[[1, 2], [3, 4], [5, 6]]):92    print(f"  Row {i0}: a={a1}, b={b2}")
    output  Row 0: a=1, b=2
    All 3 passes — pass 1 is the card above
    passiab
    1012
    2134
    3256
  13. response ← {'status': 200, 'data': {'user': {'name': 'Alice', 'id': 123}, 'items': [1, 2, 3]}}

    94# JSON-like structure95print("\nJSON-like structure:")9697response→ {'status': 200, 'data': {'user': {'name': 'Alice', 'id': 123}, 'items': [1, 2, 3]}} = {98    "status": 200,99    "data": {100        "user": {"name": "Alice", "id": 123},101        "items": [1, 2, 3]102    }103}104105# Extract nested values106status→ 200 = response["status"]200107user_data→ {'name': 'Alice', 'id': 123} = response["data"]["user"]{'name': 'Alice', 'id': 123}108name→ Alice, user_id→ 123 = user_data["name"]Alice, user_data["id"]123109110print(f"status={status200}, user={nameAlice} (id={user_id123})")111112# Complex nesting113print("\nComplex nesting:")114115# Table data116table→ [['Name', 'Scores'], ['Alice', [85, 90, 92]], ['Bob', [78, 88, 85]]] = [117    ["Name", "Scores"],118    ["Alice", [85, 90, 92]],119    ["Bob", [78, 88, 85]]120]121122header→ ['Name', 'Scores'], *rows→ [['Alice', [85, 90, 92]], ['Bob', [78, 88, 85]]] = table[['Name', 'Scores'], ['Alice', [85, 90, 92]], ['Bob', [78, 88, 85]]]123[name_col→ Name, scores_col→ Scores] = header['Name', 'Scores']124125print(f"Columns: {name_colName}, {scores_colScores}")126print("Data:")127for name, scores in rows:
    output
    JSON-like structure:
    status=200, user=Alice (id=123)
    
    Complex nesting:
    Columns: Name, Scores
    Data:
  14. first ← 85, rest ← [90, 92]

    pass 1 of 2
    126print("Data:")127for nameAlice, scores[85, 90, 92] in rows[['Alice', [85, 90, 92]], ['Bob', [78, 88, 85]]]:128    first→ 85, *rest→ [90, 92] = scores[85, 90, 92]129    print(f"  {nameAlice}: first={first85}, rest={rest[90, 92]}")
    output  Alice: first=85, rest=[90, 92]
  15. first ← 78, rest ← [88, 85]

    pass 2 of 2
    126print("Data:")127for nameBob, scores[78, 88, 85] in rows[['Alice', [85, 90, 92]], ['Bob', [78, 88, 85]]]:128    first→ 78, *rest→ [88, 85] = scores[78, 88, 85]129    print(f"  {nameBob}: first={first78}, rest={rest[88, 85]}")
    output  Bob: first=78, rest=[88, 85]
  16. tree ← (5, (3, None, None), (7, (6, None, None), (9, None, None)))

    131# Tree structure132print("\nTree structure:")133134# Binary tree node: (value, left, right)135tree→ (5, (3, None, None), (7, (6, None, None), (9, None, None))) = (5, (3, None, None), (7, (6, None, None), (9, None, None)))136137value→ 5, left→ (3, None, None), right→ (7, (6, None, None), (9, None, None)) = tree(5, (3, None, None), (7, (6, None, None), (9, None, None)))138print(f"Root: {value5}")
    output
    Tree structure:
    Root: 5
  17. left_val ← 3, _ ← None

    140if left(3, None, None):141    left_val→ 3, _→ None, _ = left(3, None, None)142    print(f"Left child: {left_val3}")
    outputLeft child: 3
  18. right_val ← 7, right_left ← (6, None, None), right_right ← (9, None, None)

    144if right(7, (6, None, None), (9, None, None)):145    right_val→ 7, right_left→ (6, None, None), right_right→ (9, None, None) = right(7, (6, None, None), (9, None, None))146    print(f"Right child: {right_val7}")147    if right_left:
    outputRight child: 7
  19. if right_left:

    146print(f"Right child: {right_val}")147if right_left(6, None, None):148    print(f"Right-left grandchild: {right_left[0]6}")
    outputRight-left grandchild: 6
  20. csv_data ← [('Alice', '30', 'Engineer,Senior,NYC'), ('Bob', '25', 'Designer,Junior,LA')]

    150# Practical example151print("\nPractical example:")152153# Parse nested CSV154csv_data→ [('Alice', '30', 'Engineer,Senior,NYC'), ('Bob', '25', 'Designer,Junior,LA')] = [155    ("Alice", "30", "Engineer,Senior,NYC"),156    ("Bob", "25", "Designer,Junior,LA")157]
    output
    Practical example:
  21. job ← Engineer, level ← Senior, city ← NYC

    pass 1 of 2
    159for nameAlice, age30, job_infoEngineer,Senior,NYC in csv_data[('Alice', '30', 'Engineer,Senior,NYC'), ('Bob', '25', 'Designer,Junior,LA')]:160    job→ Engineer, level→ Senior, city→ NYC = job_infoEngineer,Senior,NYC.split(',')161    print(f"  {nameAlice} ({age30}): {levelSenior} {jobEngineer} in {cityNYC}")
    output  Alice (30): Senior Engineer in NYC
  22. job ← Designer, level ← Junior, city ← LA

    pass 2 of 2
    159for nameBob, age25, job_infoDesigner,Junior,LA in csv_data[('Alice', '30', 'Engineer,Senior,NYC'), ('Bob', '25', 'Designer,Junior,LA')]:160    job→ Designer, level→ Junior, city→ LA = job_infoDesigner,Junior,LA.split(',')161    print(f"  {nameBob} ({age25}): {levelJunior} {jobDesigner} in {cityLA}")
    output  Bob (25): Junior Designer in LA
  23. regions ← [('North', [(0, 0), (10, 0), (10, 10), (0, 10)]), ('South', [(0, -10), (10, -10), (10, 0), (0, 0)])]

    163# Nested coordinates164regions→ [('North', [(0, 0), (10, 0), (10, 10), (0, 10)]), ('South', [(0, -10), (10, -10), (10, 0), (0, 0)])] = [165    ("North", [(0, 0), (10, 0), (10, 10), (0, 10)]),166    ("South", [(0, -10), (10, -10), (10, 0), (0, 0)])167]
  24. x1 ← 0, y1 ← 0, x2 ← 10, y2 ← 0, x3 ← 10, y3 ← 10, x4 ← 0, y4 ← 10

    pass 1 of 2
    169for region_nameNorth, corners[(0, 0), (10, 0), (10, 10), (0, 10)] in regions[('North', [(0, 0), (10, 0), (10, 10), (0, 10)]), ('South', [(0, -10), (10, -10), (10, 0), (0, 0)])]:170    (x1→ 0, y1→ 0), (x2→ 10, y2→ 0), (x3→ 10, y3→ 10), (x4→ 0, y4→ 10) = corners[(0, 0), (10, 0), (10, 10), (0, 10)]171    print(f"  {region_nameNorth}: corners at ({x10},{y10}), ({x210},{y20}), ({x310},{y310}), ({x40},{y410})")
    output  North: corners at (0,0), (10,0), (10,10), (0,10)
  25. x1 ← 0, y1 ← -10, x2 ← 10, y2 ← -10, x3 ← 10, y3 ← 0, x4 ← 0, y4 ← 0

    pass 2 of 2
    169for region_nameSouth, corners[(0, -10), (10, -10), (10, 0), (0, 0)] in regions[('North', [(0, 0), (10, 0), (10, 10), (0, 10)]), ('South', [(0, -10), (10, -10), (10, 0), (0, 0)])]:170    (x1→ 0, y1→ -10), (x2→ 10, y2→ -10), (x3→ 10, y3→ 0), (x4→ 0, y4→ 0) = corners[(0, -10), (10, -10), (10, 0), (0, 0)]171    print(f"  {region_nameSouth}: corners at ({x10},{y1-10}), ({x210},{y2-10}), ({x310},{y30}), ({x40},{y40})")
    output  South: corners at (0,-10), (10,-10), (10,0), (0,0)

Nested unpacking is powerful for extracting data from complex structures like coordinate pairs, database rows, or API responses.

nested unpacking Unpacking multi-level data structures by matching the nesting pattern in the assignment target.

Function Argument Unpacking

function_args_unpacking.py
Replay: real traced execution (multi-file project)
"""Function argument unpacking"""

# Basic *args unpacking
print("Basic *args unpacking:")

def add(a, b, c):
    return a + b + c

numbers = [1, 2, 3]
result = add(*numbers)  # Unpacks list into arguments
print(f"add(*{numbers}) = {result}")

# Tuple unpacking
point = (10, 20, 30)
result = add(*point)
print(f"add(*{point}) = {result}")

# Range unpacking
print("\nRange unpacking:")

# Create range from list
params = [1, 10, 2]
numbers = list(range(*params))  # range(1, 10, 2)
print(f"range(*{params}) = {numbers}")

# Print unpacking
print("\nPrint unpacking:")

# Print all elements
items = ["apple", "banana", "cherry"]
print("Items:", *items)

# Print with separator
print(*items, sep=", ")
print(*items, sep=" | ")

# Variable arguments
print("\nVariable arguments:")

def concatenate(*args):
    return "".join(args)

words = ["Hello", " ", "World"]
result = concatenate(*words)
print(f"concatenate(*{words}) = '{result}'")

# **kwargs unpacking
print("\n**kwargs unpacking:")

def greet(name, age, city):
    return f"{name}, {age} years old, from {city}"

user = {"name": "Alice", "age": 30, "city": "NYC"}
message = greet(**user)  # Unpacks dict into keyword arguments
print(f"greet(**user) = {message}")

# Partial kwargs
print("\nPartial kwargs:")

def create_user(name, age=18, city="Unknown"):
    return {"name": name, "age": age, "city": city}

# Provide some kwargs
info = {"age": 25, "city": "LA"}
user = create_user("Bob", **info)
print(f"User: {user}")

# Merging dicts
print("\nMerging dicts:")

defaults = {"host": "localhost", "port": 8080, "debug": False}
overrides = {"port": 3000, "debug": True}

# Merge using **
config = {**defaults, **overrides}
print(f"defaults: {defaults}")
print(f"overrides: {overrides}")
print(f"merged: {config}")

# Function with *args and **kwargs
print("\nFunction with *args and **kwargs:")

def process(*args, **kwargs):
    print(f"  args: {args}")
    print(f"  kwargs: {kwargs}")

values = [1, 2, 3]
options = {"mode": "fast", "verbose": True}

process(*values, **options)

# Combining unpacking
print("\nCombining unpacking:")

def calculate(a, b, c, operation="add", precision=2):
    if operation == "add":
        result = a + b + c
    elif operation == "multiply":
        result = a * b * c
    return round(result, precision)

numbers = [2, 3, 4]
options = {"operation": "multiply", "precision": 1}

result = calculate(*numbers, **options)
print(f"calculate(*{numbers}, **{options}) = {result}")

# String formatting
print("\nString formatting:")

template = "{name} is {age} years old and lives in {city}"
data = {"name": "Alice", "age": 30, "city": "NYC"}

formatted = template.format(**data)
print(f"Formatted: {formatted}")

# Constructor unpacking
print("\nConstructor unpacking:")

class Point:
    def __init__(self, x, y, z=0):
        self.x = x
        self.y = y
        self.z = z

    def __repr__(self):
        return f"Point({self.x}, {self.y}, {self.z})"

# From tuple
coords = (10, 20)
p1 = Point(*coords)
print(f"p1 = {p1}")

# From dict
coords_dict = {"x": 5, "y": 15, "z": 25}
p2 = Point(**coords_dict)
print(f"p2 = {p2}")

# Forwarding arguments
print("\nForwarding arguments:")

def logged_function(func):
    def wrapper(*args, **kwargs):
        print(f"  Calling {func.__name__} with args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"  Result: {result}")
        return result
    return wrapper

@logged_function
def add_numbers(a, b, c=0):
    return a + b + c

add_numbers(1, 2, c=3)

# API calls
print("\nAPI calls:")

def make_request(url, method="GET", headers=None, params=None):
    print(f"  {method} {url}")
    if headers:
        print(f"  Headers: {headers}")
    if params:
        print(f"  Params: {params}")

request_config = {
    "method": "POST",
    "headers": {"Content-Type": "application/json"},
    "params": {"key": "value"}
}

make_request("https://api.example.com/users", **request_config)

# Practical example
print("\nPractical example:")

# Database query builder
def build_query(table, fields=None, where=None, limit=None):
    query = f"SELECT "
    query += ", ".join(fields) if fields else "*"
    query += f" FROM {table}"

    if where:
        conditions = " AND ".join(f"{k}='{v}'" for k, v in where.items())
        query += f" WHERE {conditions}"

    if limit:
        query += f" LIMIT {limit}"

    return query

# Build query with unpacking
query_params = {
    "fields": ["name", "email"],
    "where": {"age": 30, "city": "NYC"},
    "limit": 10
}

query = build_query("users", **query_params)
print(f"Query: {query}")

  1. numbers ← [1, 2, 3]

    1"""Function argument unpacking"""23# Basic *args unpacking4print("Basic *args unpacking:")56def add(a, b, c):7    return a + b + c89numbers→ [1, 2, 3] = [1, 2, 3]10result = add(*numbers[1, 2, 3])  # Unpacks list into arguments11print(f"add(*{numbers}) = {result}")
    outputBasic *args unpacking:
  2. def add(a, b, c):

    pass 1 of 2
    6def add(a1, b2, c3):7    return a1 + b2 + c3
  3. result ← 6, point ← (10, 20, 30)

    9numbers = [1, 2, 3]10result→ 6 = add(*numbers[1, 2, 3])  # Unpacks list into arguments11print(f"add(*{numbers[1, 2, 3]}) = {result6}")1213# Tuple unpacking14point→ (10, 20, 30) = (10, 20, 30)15result = add(*point(10, 20, 30))16print(f"add(*{point}) = {result}")
    outputadd(*[1, 2, 3]) = 6
  4. def add(a, b, c):

    pass 2 of 2
    6def add(a10, b20, c30):7    return a10 + b20 + c30
  5. result ← 60, params ← [1, 10, 2], numbers ← [1, 3, 5, 7, 9], items ← ['apple', 'banana', 'cherry']

    14point = (10, 20, 30)15result→ 60 = add(*point(10, 20, 30))16print(f"add(*{point(10, 20, 30)}) = {result60}")1718# Range unpacking19print("\nRange unpacking:")2021# Create range from list22params→ [1, 10, 2] = [1, 10, 2]23numbers→ [1, 3, 5, 7, 9] = list(range(*params[1, 10, 2]))  # range(1, 10, 2)24print(f"range(*{params[1, 10, 2]}) = {numbers[1, 3, 5, 7, 9]}")2526# Print unpacking27print("\nPrint unpacking:")2829# Print all elements30items→ ['apple', 'banana', 'cherry'] = ["apple", "banana", "cherry"]31print("Items:", *items['apple', 'banana', 'cherry'])3233# Print with separator34print(*items['apple', 'banana', 'cherry'], sep=", ")35print(*items['apple', 'banana', 'cherry'], sep=" | ")3637# Variable arguments38print("\nVariable arguments:")3940def concatenate(*args):41    return "".join(args)4243words→ ['Hello', ' ', 'World'] = ["Hello", " ", "World"]44result = concatenate(*words['Hello', ' ', 'World'])45print(f"concatenate(*{words}) = '{result}'")
    outputadd(*(10, 20, 30)) = 60
    
    Range unpacking:
    range(*[1, 10, 2]) = [1, 3, 5, 7, 9]
    
    Print unpacking:
    Items: apple banana cherry
    apple banana cherry
    apple banana cherry
    
    Variable arguments:
  6. def concatenate(*args):

    40def concatenate(*args('Hello', ' ', 'World')):41    return "".join(args('Hello', ' ', 'World'))
  7. result ← Hello World, user ← {'name': 'Alice', 'age': 30, 'city': 'NYC'}

    43words = ["Hello", " ", "World"]44result→ Hello World = concatenate(*words['Hello', ' ', 'World'])45print(f"concatenate(*{words['Hello', ' ', 'World']}) = '{resultHello World}'")4647# **kwargs unpacking48print("\n**kwargs unpacking:")4950def greet(name, age, city):51    return f"{name}, {age} years old, from {city}"5253user→ {'name': 'Alice', 'age': 30, 'city': 'NYC'} = {"name": "Alice", "age": 30, "city": "NYC"}54message = greet(**user{'name': 'Alice', 'age': 30, 'city': 'NYC'})  # Unpacks dict into keyword arguments55print(f"greet(**user) = {message}")
    outputconcatenate(*['Hello', ' ', 'World']) = 'Hello World'
    
    **kwargs unpacking:
  8. def greet(name, age, city):

    50def greet(nameAlice, age30, cityNYC):51    return f"{nameAlice}, {age30} years old, from {cityNYC}"
  9. message ← Alice, 30 years old, from NYC, info ← {'age': 25, 'city': 'LA'}

    53user = {"name": "Alice", "age": 30, "city": "NYC"}54message→ Alice, 30 years old, from NYC = greet(**user{'name': 'Alice', 'age': 30, 'city': 'NYC'})  # Unpacks dict into keyword arguments55print(f"greet(**user) = {messageAlice, 30 years old, from NYC}")5657# Partial kwargs58print("\nPartial kwargs:")5960def create_user(name, age=18, city="Unknown"):61    return {"name": name, "age": age, "city": city}6263# Provide some kwargs64info→ {'age': 25, 'city': 'LA'} = {"age": 25, "city": "LA"}65user = create_user("Bob", **info{'age': 25, 'city': 'LA'})66print(f"User: {user}")
    outputgreet(**user) = Alice, 30 years old, from NYC
    
    Partial kwargs:
  10. def create_user(name, age=18, city="Unknown"):

    60def create_user(nameBob, age25=18, cityLA="Unknown"):61    return {"name": nameBob, "age": age25, "city": cityLA}
  11. user ← {'name': 'Bob', 'age': 25, 'city': 'LA'}, defaults ← {'host': 'localhost', 'port': 8080, 'debug': False}

    64info = {"age": 25, "city": "LA"}65user→ {'name': 'Bob', 'age': 25, 'city': 'LA'} = create_user("Bob", **info{'age': 25, 'city': 'LA'})66print(f"User: {user{'name': 'Bob', 'age': 25, 'city': 'LA'}}")6768# Merging dicts69print("\nMerging dicts:")7071defaults→ {'host': 'localhost', 'port': 8080, 'debug': False} = {"host": "localhost", "port": 8080, "debug": False}72overrides→ {'port': 3000, 'debug': True} = {"port": 3000, "debug": True}7374# Merge using **75config→ {'host': 'localhost', 'port': 3000, 'debug': True} = {**defaults{'host': 'localhost', 'port': 8080, 'debug': False}, **overrides{'port': 3000, 'debug': True}}76print(f"defaults: {defaults{'host': 'localhost', 'port': 8080, 'debug': False}}")77print(f"overrides: {overrides{'port': 3000, 'debug': True}}")78print(f"merged: {config{'host': 'localhost', 'port': 3000, 'debug': True}}")7980# Function with *args and **kwargs81print("\nFunction with *args and **kwargs:")8283def process(*args, **kwargs):84    print(f"  args: {args}")85    print(f"  kwargs: {kwargs}")8687values→ [1, 2, 3] = [1, 2, 3]88options→ {'mode': 'fast', 'verbose': True} = {"mode": "fast", "verbose": True}8990process(*values[1, 2, 3], **options{'mode': 'fast', 'verbose': True})
    outputUser: {'name': 'Bob', 'age': 25, 'city': 'LA'}
    
    Merging dicts:
    defaults: {'host': 'localhost', 'port': 8080, 'debug': False}
    overrides: {'port': 3000, 'debug': True}
    merged: {'host': 'localhost', 'port': 3000, 'debug': True}
    
    Function with *args and **kwargs:
  12. def process(*args, **kwargs):

    83def process(*args(1, 2, 3), **kwargs):84    print(f"  args: {args(1, 2, 3)}")85    print(f"  kwargs: {kwargs{'mode': 'fast', 'verbose': True}}")
    output  args: (1, 2, 3)
      kwargs: {'mode': 'fast', 'verbose': True}
  13. numbers ← [2, 3, 4], options ← {'operation': 'multiply', 'precision': 1}

    90process(*values[1, 2, 3], **options{'mode': 'fast', 'verbose': True})9192# Combining unpacking93print("\nCombining unpacking:")9495def calculate(a, b, c, operation="add", precision=2):96    if operation == "add":97        result = a + b + c98    elif operation == "multiply":99        result = a * b * c100    return round(result, precision)101102numbers→ [2, 3, 4] = [2, 3, 4]103options→ {'operation': 'multiply', 'precision': 1} = {"operation": "multiply", "precision": 1}104105result = calculate(*numbers[2, 3, 4], **options{'operation': 'multiply', 'precision': 1})106print(f"calculate(*{numbers}, **{options}) = {result}")
    output
    Combining unpacking:
  14. def calculate(a, b, c, operation="add", precision=2):

    95def calculate(a2, b3, c4, operationmultiply="add", precision1=2):96    if operation == "add":97        result = a + b + c
  15. result ← 24

    97    result = a + b + c98elif operationmultiply == "multiply":99    result→ 24 = a2 * b3 * c4100return round(result, precision)
  16. return round(result, precision)

    99    result = a * b * c100return round(result24, precision1)
  17. result ← 24, template ← {name} is {age} years old and lives in {city}

    105result→ 24 = calculate(*numbers[2, 3, 4], **options{'operation': 'multiply', 'precision': 1})106print(f"calculate(*{numbers[2, 3, 4]}, **{options{'operation': 'multiply', 'precision': 1}}) = {result24}")107108# String formatting109print("\nString formatting:")110111template→ {name} is {age} years old and lives in {city} = "{name} is {age} years old and lives in {city}"112data→ {'name': 'Alice', 'age': 30, 'city': 'NYC'} = {"name": "Alice", "age": 30, "city": "NYC"}113114formatted→ Alice is 30 years old and lives in NYC = template{name} is {age} years old and lives in {city}.format(**data{'name': 'Alice', 'age': 30, 'city': 'NYC'})115print(f"Formatted: {formattedAlice is 30 years old and lives in NYC}")116117# Constructor unpacking118print("\nConstructor unpacking:")119120class Point:121    def __init__(self, x, y, z=0):122        self.x = x123        self.y = y124        self.z = z125    126    def __repr__(self):127        return f"Point({self.x}, {self.y}, {self.z})"128129# From tuple130coords→ (10, 20) = (10, 20)131p1 = Point(*coords(10, 20))132print(f"p1 = {p1}")
    outputcalculate(*[2, 3, 4], **{'operation': 'multiply', 'precision': 1}) = 24
    
    String formatting:
    Formatted: Alice is 30 years old and lives in NYC
    
    Constructor unpacking:
  18. self.x ← 10, self.y ← 20, self.z ← 0

    pass 1 of 2
    120class Point:121    def __init__(self(empty), x10, y20, z0=0):122        self.x→ 10 = x10123        self.y→ 20 = y20124        self.z→ 0 = z0
  19. p1 ← Point(10, 20, 0), coords_dict ← {'x': 5, 'y': 15, 'z': 25}

    130coords = (10, 20)131p1→ Point(10, 20, 0) = Point(*coords(10, 20))132print(f"p1 = {p1Point(10, 20, 0)}")133134# From dict135coords_dict→ {'x': 5, 'y': 15, 'z': 25} = {"x": 5, "y": 15, "z": 25}136p2 = Point(**coords_dict{'x': 5, 'y': 15, 'z': 25})137print(f"p2 = {p2}")
    outputp1 = Point(10, 20, 0)
  20. self.x ← 5, self.y ← 15, self.z ← 25

    pass 2 of 2
    120class Point:121    def __init__(self(empty), x5, y15, z25=0):122        self.x→ 5 = x5123        self.y→ 15 = y15124        self.z→ 25 = z25
  21. p2 ← Point(5, 15, 25)

    135coords_dict = {"x": 5, "y": 15, "z": 25}136p2→ Point(5, 15, 25) = Point(**coords_dict{'x': 5, 'y': 15, 'z': 25})137print(f"p2 = {p2Point(5, 15, 25)}")138139# Forwarding arguments140print("\nForwarding arguments:")
    outputp2 = Point(5, 15, 25)
    
    Forwarding arguments:
  22. def logged_function(func):

    142def logged_function(func⟨function add_numbers A⟩):143    def wrapper(*args, **kwargs):144        print(f"  Calling {func.__name__} with args={args}, kwargs={kwargs}")145        result = func(*args, **kwargs)146        print(f"  Result: {result}")147        return result148    return wrapper<function logged_function.<locals>.wrapper at ⟨addr B⟩>
  23. add_numbers(1, 2, c=3)

    154add_numbers(1, 2, c=3)
  24. def wrapper(*args, **kwargs):

    142def logged_function(func):143    def wrapper(*args(1, 2), **kwargs):144        print(f"  Calling {func.__name__add_numbers} with args={args(1, 2)}, kwargs={kwargs{'c': 3}}")145        result = func(*args(1, 2), **kwargs{'c': 3})146        print(f"  Result: {result}")
    output  Calling add_numbers with args=(1, 2), kwargs={'c': 3}
  25. def add_numbers(a, b, c=0):

    150@logged_function151def add_numbers(a1, b2, c3=0):152    return a1 + b2 + c3
  26. result ← 6

    144    print(f"  Calling {func.__name__} with args={args}, kwargs={kwargs}")145    result→ 6 = func(*args(1, 2), **kwargs{'c': 3})146    print(f"  Result: {result6}")147    return result6148return wrapper
    output  Result: 6
  27. request_config ← {'method': 'POST', 'headers': {'Content-Type': 'application/json'}, 'params': {'key': 'value'}}

    154add_numbers(1, 2, c=3)155156# API calls157print("\nAPI calls:")158159def make_request(url, method="GET", headers=None, params=None):160    print(f"  {method} {url}")161    if headers:162        print(f"  Headers: {headers}")163    if params:164        print(f"  Params: {params}")165166request_config→ {'method': 'POST', 'headers': {'Content-Type': 'application/json'}, 'params': {'key': 'value'}} = {167    "method": "POST",168    "headers": {"Content-Type": "application/json"},169    "params": {"key": "value"}170}171172make_request("https://api.example.com/users", **request_config{'method': 'POST', 'headers': {'Content-Type': 'application/json'}, 'params': {'key': 'value'}})
    output
    API calls:
  28. def make_request(url, method="GET", headers=None, params=None):

    159def make_request(urlhttps://api.example.com/users, methodPOST="GET", headers{'Content-Type': 'application/json'}=NoneNone, params{'key': 'value'}=None):160    print(f"  {methodPOST} {urlhttps://api.example.com/users}")161    if headers:
    output  POST https://api.example.com/users
  29. if headers:

    160print(f"  {method} {url}")161if headers{'Content-Type': 'application/json'}:162    print(f"  Headers: {headers{'Content-Type': 'application/json'}}")163if params:
    output  Headers: {'Content-Type': 'application/json'}
  30. if params:

    162    print(f"  Headers: {headers}")163if params{'key': 'value'}:164    print(f"  Params: {params{'key': 'value'}}")
    output  Params: {'key': 'value'}
  31. query_params ← {'fields': ['name', 'email'], 'where': {'age': 30, 'city': 'NYC'}, 'limit': 10}

    172make_request("https://api.example.com/users", **request_config{'method': 'POST', 'headers': {'Content-Type': 'application/json'}, 'params': {'key': 'value'}})173174# Practical example175print("\nPractical example:")176177# Database query builder178def build_query(table, fields=None, where=None, limit=None):179    query = f"SELECT "180    query += ", ".join(fields) if fields else "*"181    query += f" FROM {table}"182    183    if where:184        conditions = " AND ".join(f"{k}='{v}'" for k, v in where.items())185        query += f" WHERE {conditions}"186    187    if limit:188        query += f" LIMIT {limit}"189    190    return query191192# Build query with unpacking193query_params→ {'fields': ['name', 'email'], 'where': {'age': 30, 'city': 'NYC'}, 'limit': 10} = {194    "fields": ["name", "email"],195    "where": {"age": 30, "city": "NYC"},196    "limit": 10197}198199query = build_query("users", **query_params{'fields': ['name', 'email'], 'where': {'age': 30, 'city': 'NYC'}, 'limit': 10})200print(f"Query: {query}")
    output
    Practical example:
  32. query ← SELECT

    177# Database query builder178def build_query(tableusers, fields['name', 'email']=NoneNone, where{'age': 30, 'city': 'NYC'}=None, limit10=None):179    query→ SELECT  = f"SELECT "180    query→ SELECT name, email += ", ".join(fields['name', 'email']) if fields else "*"181    query→ SELECT name, email FROM users += f" FROM {tableusers}"
  33. conditions ← age='30' AND city='NYC', query ← SELECT name, email FROM users WHERE age='30' AND city='NYC'

    183if where{'age': 30, 'city': 'NYC'}:184    conditions→ age='30' AND city='NYC' = " AND ".join(f"{k(empty)}='{v(empty)}'" for k, v in where{'age': 30, 'city': 'NYC'}.items())185    query→ SELECT name, email FROM users WHERE age='30' AND city='NYC' += f" WHERE {conditionsage='30' AND city='NYC'}"
  34. query ← SELECT name, email FROM users WHERE age='30' AND city='NYC' LIMIT 10

    187if limit10:188    query→ SELECT name, email FROM users WHERE age='30' AND city='NYC' LIMIT 10 += f" LIMIT {limit10}"
  35. return query

    190return querySELECT name, email FROM users WHERE age='30' AND city='NYC' LIMIT 10
  36. query ← SELECT name, email FROM users WHERE age='30' AND city='NYC' LIMIT 10

    199query→ SELECT name, email FROM users WHERE age='30' AND city='NYC' LIMIT 10 = build_query("users", **query_params{'fields': ['name', 'email'], 'where': {'age': 30, 'city': 'NYC'}, 'limit': 10})200print(f"Query: {querySELECT name, email FROM users WHERE age='30' AND city='NYC' LIMIT 10}")
    outputQuery: SELECT name, email FROM users WHERE age='30' AND city='NYC' LIMIT 10

This pattern is essential for building flexible APIs, decorators, and configuration systems.

argument unpacking Using `*` to expand sequences into positional arguments and `**` to expand dicts into keyword arguments when calling functions.

Dictionary Unpacking

dict_unpacking.py
Replay: real traced execution (multi-file project)
"""Dictionary unpacking examples"""

# Basic dict unpacking
print("Basic dict unpacking:")

user1 = {"name": "Alice", "age": 30}
user2 = {"city": "NYC", "job": "Engineer"}

# Merge dicts
merged = {**user1, **user2}
print(f"user1: {user1}")
print(f"user2: {user2}")
print(f"merged: {merged}")

# Overwriting values
print("\nOverwriting values:")

defaults = {"host": "localhost", "port": 8080, "debug": False}
custom = {"port": 3000, "timeout": 30}

# Later values overwrite earlier ones
config = {**defaults, **custom}
print(f"defaults: {defaults}")
print(f"custom: {custom}")
print(f"config: {config}")

# Multiple merges
print("\nMultiple merges:")

base = {"a": 1, "b": 2}
middle = {"b": 20, "c": 3}
final = {"c": 30, "d": 4}

# Merge three dicts
result = {**base, **middle, **final}
print(f"base: {base}")
print(f"middle: {middle}")
print(f"final: {final}")
print(f"result: {result}")

# Adding new keys
print("\nAdding new keys:")

user = {"name": "Bob", "age": 25}

# Add new key while merging
updated = {**user, "email": "bob@example.com"}
print(f"original: {user}")
print(f"updated: {updated}")

# Update existing key
modified = {**user, "age": 26}
print(f"modified: {modified}")

# Conditional merging
print("\nConditional merging:")

user = {"name": "Alice", "age": 30}
admin_fields = {"role": "admin", "permissions": ["read", "write"]}

is_admin = True
profile = {
    **user,
    **(admin_fields if is_admin else {})
}

print(f"profile (admin={is_admin}): {profile}")

is_admin = False
profile = {
    **user,
    **(admin_fields if is_admin else {})
}
print(f"profile (admin={is_admin}): {profile}")

# Function kwargs
print("\nFunction kwargs:")

def create_config(**kwargs):
    defaults = {"host": "localhost", "port": 8080, "debug": False}
    return {**defaults, **kwargs}

config1 = create_config(port=3000)
print(f"config1: {config1}")

config2 = create_config(host="0.0.0.0", debug=True)
print(f"config2: {config2}")

# Copying with modification
print("\nCopying with modification:")

original = {"name": "Alice", "age": 30, "city": "NYC"}

# Copy and modify
modified = {**original, "age": 31}
print(f"original: {original}")
print(f"modified: {modified}")

# Remove key (using dict comprehension)
without_age = {k: v for k, v in {**original}.items() if k != "age"}
print(f"without_age: {without_age}")

# Nested dict merging
print("\nNested dict merging:")

# Shallow merge (doesn't merge nested dicts)
dict1 = {"user": {"name": "Alice", "age": 30}}
dict2 = {"user": {"email": "alice@example.com"}}

merged = {**dict1, **dict2}
print(f"Shallow merge: {merged}")
print("  Note: dict2['user'] completely replaced dict1['user']")

# Deep merge (manual)
def deep_merge(dict1, dict2):
    result = dict1.copy()
    for key, value in dict2.items():
        if key in result and isinstance(result[key], dict) and isinstance(value, dict):
            result[key] = deep_merge(result[key], value)
        else:
            result[key] = value
    return result

dict1 = {"user": {"name": "Alice", "age": 30}}
dict2 = {"user": {"email": "alice@example.com"}}
deep_merged = deep_merge(dict1, dict2)
print(f"Deep merge: {deep_merged}")

# Building objects
print("\nBuilding objects:")

class User:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def __repr__(self):
        return f"User({', '.join(f'{k}={v!r}' for k, v in self.__dict__.items())})"

base_user = {"name": "Bob", "age": 25}
user = User(**base_user, city="LA")
print(f"user: {user}")

# Environment variables
print("\nEnvironment variables:")

# Simulating environment variables
env_vars = {"DB_HOST": "localhost", "DB_PORT": "5432"}
default_vars = {"DB_HOST": "127.0.0.1", "DB_PORT": "5432", "DB_NAME": "mydb"}

# Env vars override defaults
config = {**default_vars, **env_vars}
print(f"config: {config}")

# API responses
print("\nAPI responses:")

def make_response(data, **meta):
    return {
        "data": data,
        **meta
    }

response1 = make_response({"users": [1, 2, 3]}, status=200)
print(f"response1: {response1}")

response2 = make_response({"error": "Not found"}, status=404, timestamp=1234567890)
print(f"response2: {response2}")

# Practical example
print("\nPractical example:")

# Configuration builder
class Config:
    def __init__(self):
        self.settings = {}

    def add_section(self, name, **settings):
        self.settings[name] = {**self.settings.get(name, {}), **settings}

    def get(self):
        return self.settings

config = Config()
config.add_section("database", host="localhost", port=5432)
config.add_section("database", username="admin", password="secret")
config.add_section("server", host="0.0.0.0", port=8080)

print("Final config:")
for section, settings in config.get().items():
    print(f"  [{section}]")
    for key, value in settings.items():
        print(f"    {key}={value}")

# Merging user preferences
default_prefs = {
    "theme": "light",
    "font_size": 12,
    "auto_save": True,
    "line_numbers": True
}

user_prefs = {
    "theme": "dark",
    "font_size": 14
}

final_prefs = {**default_prefs, **user_prefs}
print(f"\nFinal preferences: {final_prefs}")

  1. user1 ← {'name': 'Alice', 'age': 30}, user2 ← {'city': 'NYC', 'job': 'Engineer'}

    1"""Dictionary unpacking examples"""23# Basic dict unpacking4print("Basic dict unpacking:")56user1→ {'name': 'Alice', 'age': 30} = {"name": "Alice", "age": 30}7user2→ {'city': 'NYC', 'job': 'Engineer'} = {"city": "NYC", "job": "Engineer"}89# Merge dicts10merged→ {'name': 'Alice', 'age': 30, 'city': 'NYC', 'job': 'Engineer'} = {**user1{'name': 'Alice', 'age': 30}, **user2{'city': 'NYC', 'job': 'Engineer'}}11print(f"user1: {user1{'name': 'Alice', 'age': 30}}")12print(f"user2: {user2{'city': 'NYC', 'job': 'Engineer'}}")13print(f"merged: {merged{'name': 'Alice', 'age': 30, 'city': 'NYC', 'job': 'Engineer'}}")1415# Overwriting values16print("\nOverwriting values:")1718defaults→ {'host': 'localhost', 'port': 8080, 'debug': False} = {"host": "localhost", "port": 8080, "debug": False}19custom→ {'port': 3000, 'timeout': 30} = {"port": 3000, "timeout": 30}2021# Later values overwrite earlier ones22config→ {'host': 'localhost', 'port': 3000, 'debug': False, 'timeout': 30} = {**defaults{'host': 'localhost', 'port': 8080, 'debug': False}, **custom{'port': 3000, 'timeout': 30}}23print(f"defaults: {defaults{'host': 'localhost', 'port': 8080, 'debug': False}}")24print(f"custom: {custom{'port': 3000, 'timeout': 30}}")25print(f"config: {config{'host': 'localhost', 'port': 3000, 'debug': False, 'timeout': 30}}")2627# Multiple merges28print("\nMultiple merges:")2930base→ {'a': 1, 'b': 2} = {"a": 1, "b": 2}31middle→ {'b': 20, 'c': 3} = {"b": 20, "c": 3}32final→ {'c': 30, 'd': 4} = {"c": 30, "d": 4}3334# Merge three dicts35result→ {'a': 1, 'b': 20, 'c': 30, 'd': 4} = {**base{'a': 1, 'b': 2}, **middle{'b': 20, 'c': 3}, **final{'c': 30, 'd': 4}}36print(f"base: {base{'a': 1, 'b': 2}}")37print(f"middle: {middle{'b': 20, 'c': 3}}")38print(f"final: {final{'c': 30, 'd': 4}}")39print(f"result: {result{'a': 1, 'b': 20, 'c': 30, 'd': 4}}")4041# Adding new keys42print("\nAdding new keys:")4344user→ {'name': 'Bob', 'age': 25} = {"name": "Bob", "age": 25}4546# Add new key while merging47updated→ {'name': 'Bob', 'age': 25, 'email': 'bob@example.com'} = {**user{'name': 'Bob', 'age': 25}, "email": "bob@example.com"}48print(f"original: {user{'name': 'Bob', 'age': 25}}")49print(f"updated: {updated{'name': 'Bob', 'age': 25, 'email': 'bob@example.com'}}")5051# Update existing key52modified→ {'name': 'Bob', 'age': 26} = {**user{'name': 'Bob', 'age': 25}, "age": 26}53print(f"modified: {modified{'name': 'Bob', 'age': 26}}")5455# Conditional merging56print("\nConditional merging:")5758user→ {'name': 'Alice', 'age': 30} = {"name": "Alice", "age": 30}59admin_fields→ {'role': 'admin', 'permissions': ['read', 'write']} = {"role": "admin", "permissions": ["read", "write"]}6061is_admin→ True = True62profile→ {'name': 'Alice', 'age': 30, 'role': 'admin', 'permissions': ['read', 'write']} = {63    **user{'name': 'Alice', 'age': 30},64    **(admin_fields{'role': 'admin', 'permissions': ['read', 'write']} if is_adminTrue else {})65}6667print(f"profile (admin={is_adminTrue}): {profile{'name': 'Alice', 'age': 30, 'role': 'admin', 'permissions': ['read', 'write']}}")6869is_admin→ False = False70profile→ {'name': 'Alice', 'age': 30} = {71    **user{'name': 'Alice', 'age': 30},72    **(admin_fields{'role': 'admin', 'permissions': ['read', 'write']} if is_adminFalse else {})73}74print(f"profile (admin={is_adminFalse}): {profile{'name': 'Alice', 'age': 30}}")7576# Function kwargs77print("\nFunction kwargs:")7879def create_config(**kwargs):80    defaults = {"host": "localhost", "port": 8080, "debug": False}81    return {**defaults, **kwargs}8283config1 = create_config(port=3000)84print(f"config1: {config1}")
    outputBasic dict unpacking:
    user1: {'name': 'Alice', 'age': 30}
    user2: {'city': 'NYC', 'job': 'Engineer'}
    merged: {'name': 'Alice', 'age': 30, 'city': 'NYC', 'job': 'Engineer'}
    
    Overwriting values:
    defaults: {'host': 'localhost', 'port': 8080, 'debug': False}
    custom: {'port': 3000, 'timeout': 30}
    config: {'host': 'localhost', 'port': 3000, 'debug': False, 'timeout': 30}
    
    Multiple merges:
    base: {'a': 1, 'b': 2}
    middle: {'b': 20, 'c': 3}
    final: {'c': 30, 'd': 4}
    result: {'a': 1, 'b': 20, 'c': 30, 'd': 4}
    
    Adding new keys:
    original: {'name': 'Bob', 'age': 25}
    updated: {'name': 'Bob', 'age': 25, 'email': 'bob@example.com'}
    modified: {'name': 'Bob', 'age': 26}
    
    Conditional merging:
    profile (admin=True): {'name': 'Alice', 'age': 30, 'role': 'admin', 'permissions': ['read', 'write']}
    profile (admin=False): {'name': 'Alice', 'age': 30}
    
    Function kwargs:
  2. defaults ← {'host': 'localhost', 'port': 8080, 'debug': False}

    pass 1 of 2
    79def create_config(**kwargs):80    defaults→ {'host': 'localhost', 'port': 8080, 'debug': False} = {"host": "localhost", "port": 8080, "debug": False}81    return {**defaults{'host': 'localhost', 'port': 8080, 'debug': False}, **kwargs{'port': 3000}}
  3. config1 ← {'host': 'localhost', 'port': 3000, 'debug': False}

    83config1→ {'host': 'localhost', 'port': 3000, 'debug': False} = create_config(port=3000)84print(f"config1: {config1{'host': 'localhost', 'port': 3000, 'debug': False}}")8586config2 = create_config(host="0.0.0.0", debug=True)87print(f"config2: {config2}")
    outputconfig1: {'host': 'localhost', 'port': 3000, 'debug': False}
  4. defaults ← {'host': 'localhost', 'port': 8080, 'debug': False}

    pass 2 of 2
    79def create_config(**kwargs):80    defaults→ {'host': 'localhost', 'port': 8080, 'debug': False} = {"host": "localhost", "port": 8080, "debug": False}81    return {**defaults{'host': 'localhost', 'port': 8080, 'debug': False}, **kwargs{'host': '0.0.0.0', 'debug': True}}
  5. config2 ← {'host': '0.0.0.0', 'port': 8080, 'debug': True}, original ← {'name': 'Alice', 'age': 30, 'city': 'NYC'}

    86config2→ {'host': '0.0.0.0', 'port': 8080, 'debug': True} = create_config(host="0.0.0.0", debug=True)87print(f"config2: {config2{'host': '0.0.0.0', 'port': 8080, 'debug': True}}")8889# Copying with modification90print("\nCopying with modification:")9192original→ {'name': 'Alice', 'age': 30, 'city': 'NYC'} = {"name": "Alice", "age": 30, "city": "NYC"}9394# Copy and modify95modified→ {'name': 'Alice', 'age': 31, 'city': 'NYC'} = {**original{'name': 'Alice', 'age': 30, 'city': 'NYC'}, "age": 31}96print(f"original: {original{'name': 'Alice', 'age': 30, 'city': 'NYC'}}")97print(f"modified: {modified{'name': 'Alice', 'age': 31, 'city': 'NYC'}}")9899# Remove key (using dict comprehension)100without_age→ {'name': 'Alice', 'city': 'NYC'} = {k(empty): v(empty) for k, v in {**original{'name': 'Alice', 'age': 30, 'city': 'NYC'}}.items() if k != "age"}101print(f"without_age: {without_age{'name': 'Alice', 'city': 'NYC'}}")102103# Nested dict merging104print("\nNested dict merging:")105106# Shallow merge (doesn't merge nested dicts)107dict1→ {'user': {'name': 'Alice', 'age': 30}} = {"user": {"name": "Alice", "age": 30}}108dict2→ {'user': {'email': 'alice@example.com'}} = {"user": {"email": "alice@example.com"}}109110merged→ {'user': {'email': 'alice@example.com'}} = {**dict1{'user': {'name': 'Alice', 'age': 30}}, **dict2{'user': {'email': 'alice@example.com'}}}111print(f"Shallow merge: {merged{'user': {'email': 'alice@example.com'}}}")112print("  Note: dict2['user'] completely replaced dict1['user']")113114# Deep merge (manual)115def deep_merge(dict1, dict2):116    result = dict1.copy()117    for key, value in dict2.items():118        if key in result and isinstance(result[key], dict) and isinstance(value, dict):119            result[key] = deep_merge(result[key], value)120        else:121            result[key] = value122    return result123124dict1→ {'user': {'name': 'Alice', 'age': 30}} = {"user": {"name": "Alice", "age": 30}}125dict2→ {'user': {'email': 'alice@example.com'}} = {"user": {"email": "alice@example.com"}}126deep_merged = deep_merge(dict1{'user': {'name': 'Alice', 'age': 30}}, dict2{'user': {'email': 'alice@example.com'}})127print(f"Deep merge: {deep_merged}")
    outputconfig2: {'host': '0.0.0.0', 'port': 8080, 'debug': True}
    
    Copying with modification:
    original: {'name': 'Alice', 'age': 30, 'city': 'NYC'}
    modified: {'name': 'Alice', 'age': 31, 'city': 'NYC'}
    without_age: {'name': 'Alice', 'city': 'NYC'}
    
    Nested dict merging:
    Shallow merge: {'user': {'email': 'alice@example.com'}}
      Note: dict2['user'] completely replaced dict1['user']
  6. result ← {'user': {'name': 'Alice', 'age': 30}}

    pass 1 of 2
    114# Deep merge (manual)115def deep_merge(dict1{'user': {'name': 'Alice', 'age': 30}}, dict2{'user': {'email': 'alice@example.com'}}):116    result→ {'user': {'name': 'Alice', 'age': 30}} = dict1{'user': {'name': 'Alice', 'age': 30}}.copy()117    for key, value in dict2.items():
  7. for key, value in dict2.items():

    pass 1 of 2
    116result = dict1.copy()117for keyuser, value{'email': 'alice@example.com'} in dict2{'user': {'email': 'alice@example.com'}}.items():118    if key in result and isinstance(result[key], dict) and isinstance(value, dict):119        result[key] = deep_merge(result[key], value)
  8. if key in result and isinstance(result[key], dict) and isinstance(valu…

    117for key, value in dict2.items():118    if keyuser in result{'user': {'name': 'Alice', 'age': 30}} and isinstance(result[key]{'name': 'Alice', 'age': 30}, dict) and isinstance(value{'email': 'alice@example.com'}, dict):119        result[key] = deep_merge(result[key]{'name': 'Alice', 'age': 30}, value{'email': 'alice@example.com'})120    else:
  9. result ← {'name': 'Alice', 'age': 30}

    pass 2 of 2
    114# Deep merge (manual)115def deep_merge(dict1{'name': 'Alice', 'age': 30}, dict2{'email': 'alice@example.com'}):116    result→ {'name': 'Alice', 'age': 30} = dict1{'name': 'Alice', 'age': 30}.copy()117    for key, value in dict2.items():
  10. for key, value in dict2.items():

    pass 2 of 2
    116result = dict1.copy()117for keyemail, valuealice@example.com in dict2{'email': 'alice@example.com'}.items():118    if key in result and isinstance(result[key], dict) and isinstance(value, dict):119        result[key] = deep_merge(result[key], value)
  11. result[key] ← alice@example.com

    118    if key in result and isinstance(result[key], dict) and isinstance(value, dict):119        result[key] = deep_merge(result[key], value)120    else:121        result[key]→ alice@example.com = valuealice@example.com122return result
  12. return result

    121        result[key] = value122return result{'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}
  13. result[key] ← {'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}

    118if key in result and isinstance(result[key], dict) and isinstance(value, dict):119    result[key]→ {'name': 'Alice', 'age': 30, 'email': 'alice@example.com'} = deep_merge(result[key], value{'email': 'alice@example.com'})120else:
  14. return result

    121        result[key] = value122return result{'user': {'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}}
  15. deep_merged ← {'user': {'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}}

    125dict2 = {"user": {"email": "alice@example.com"}}126deep_merged→ {'user': {'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}} = deep_merge(dict1{'user': {'name': 'Alice', 'age': 30}}, dict2{'user': {'email': 'alice@example.com'}})127print(f"Deep merge: {deep_merged{'user': {'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}}}")128129# Building objects130print("\nBuilding objects:")131132class User:133    def __init__(self, **kwargs):134        self.__dict__.update(kwargs)135    136    def __repr__(self):137        return f"User({', '.join(f'{k}={v!r}' for k, v in self.__dict__.items())})"138139base_user→ {'name': 'Bob', 'age': 25} = {"name": "Bob", "age": 25}140user = User(**base_user{'name': 'Bob', 'age': 25}, city="LA")141print(f"user: {user}")
    outputDeep merge: {'user': {'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}}
    
    Building objects:
  16. self.__dict__ ← {'name': 'Bob', 'age': 25, 'city': 'LA'}

    132class User:133    def __init__(selfUser(), **kwargs):134        self.__dict__→ {'name': 'Bob', 'age': 25, 'city': 'LA'}.update(kwargs{'name': 'Bob', 'age': 25, 'city': 'LA'})
  17. user ← User(name='Bob', age=25, city='LA'), env_vars ← {'DB_HOST': 'localhost', 'DB_PORT': '5432'}

    139base_user = {"name": "Bob", "age": 25}140user→ User(name='Bob', age=25, city='LA') = User(**base_user{'name': 'Bob', 'age': 25}, city="LA")141print(f"user: {userUser(name='Bob', age=25, city='LA')}")142143# Environment variables144print("\nEnvironment variables:")145146# Simulating environment variables147env_vars→ {'DB_HOST': 'localhost', 'DB_PORT': '5432'} = {"DB_HOST": "localhost", "DB_PORT": "5432"}148default_vars→ {'DB_HOST': '127.0.0.1', 'DB_PORT': '5432', 'DB_NAME': 'mydb'} = {"DB_HOST": "127.0.0.1", "DB_PORT": "5432", "DB_NAME": "mydb"}149150# Env vars override defaults151config→ {'DB_HOST': 'localhost', 'DB_PORT': '5432', 'DB_NAME': 'mydb'} = {**default_vars{'DB_HOST': '127.0.0.1', 'DB_PORT': '5432', 'DB_NAME': 'mydb'}, **env_vars{'DB_HOST': 'localhost', 'DB_PORT': '5432'}}152print(f"config: {config{'DB_HOST': 'localhost', 'DB_PORT': '5432', 'DB_NAME': 'mydb'}}")153154# API responses155print("\nAPI responses:")156157def make_response(data, **meta):158    return {159        "data": data,160        **meta161    }162163response1 = make_response({"users": [1, 2, 3]}, status=200)164print(f"response1: {response1}")
    outputuser: User(name='Bob', age=25, city='LA')
    
    Environment variables:
    config: {'DB_HOST': 'localhost', 'DB_PORT': '5432', 'DB_NAME': 'mydb'}
    
    API responses:
  18. def make_response(data, **meta):

    pass 1 of 2
    157def make_response(data{'users': [1, 2, 3]}, **meta):158    return {159        "data": data{'users': [1, 2, 3]},160        **meta{'status': 200}161    }
  19. response1 ← {'data': {'users': [1, 2, 3]}, 'status': 200}

    163response1→ {'data': {'users': [1, 2, 3]}, 'status': 200} = make_response({"users": [1, 2, 3]}, status=200)164print(f"response1: {response1{'data': {'users': [1, 2, 3]}, 'status': 200}}")165166response2 = make_response({"error": "Not found"}, status=404, timestamp=1234567890)167print(f"response2: {response2}")
    outputresponse1: {'data': {'users': [1, 2, 3]}, 'status': 200}
  20. def make_response(data, **meta):

    pass 2 of 2
    157def make_response(data{'error': 'Not found'}, **meta):158    return {159        "data": data{'error': 'Not found'},160        **meta{'status': 404, 'timestamp': 1234567890}161    }
  21. response2 ← {'data': {'error': 'Not found'}, 'status': 404, 'timestamp': 1234567890}

    166response2→ {'data': {'error': 'Not found'}, 'status': 404, 'timestamp': 1234567890} = make_response({"error": "Not found"}, status=404, timestamp=1234567890)167print(f"response2: {response2{'data': {'error': 'Not found'}, 'status': 404, 'timestamp': 1234567890}}")168169# Practical example170print("\nPractical example:")171172# Configuration builder173class Config:174    def __init__(self):175        self.settings = {}176    177    def add_section(self, name, **settings):178        self.settings[name] = {**self.settings.get(name, {}), **settings}179    180    def get(self):181        return self.settings182183config = Config()184config.add_section("database", host="localhost", port=5432)
    outputresponse2: {'data': {'error': 'Not found'}, 'status': 404, 'timestamp': 1234567890}
    
    Practical example:
  22. self.settings ← {}

    173class Config:174    def __init__(self⟨Config A⟩):175        self.settings→ {} = {}
  23. config ← ⟨Config A⟩

    183config→ ⟨Config A⟩ = Config()184config⟨Config A⟩.add_section("database", host="localhost", port=5432)185config.add_section("database", username="admin", password="secret")
  24. self.settings ← {'database': {'host': 'localhost', 'port': 5432}}

    pass 1 of 3
    177def add_section(self⟨Config A⟩, namedatabase, **settings):178    self.settings[name]→ {'host': 'localhost', 'port': 5432} = {**self.settings→ {'database': {'host': 'localhost', 'port': 5432}}.get(namedatabase, {}), **settings{'host': 'localhost', 'port': 5432}}
    All 3 passes — pass 1 is the card above
    passnamesettingsself.settingsself.settings[name]
    1database{'host': 'localhost', 'port': 5432}{} {'database': {'host': 'localhost', 'port': 5432}}{'host': 'localhost', 'port': 5432}
    2database{'username': 'admin', 'password': 'secret'}{'database': {'host': 'localhost', 'port': 5432}} {'database': {'host': 'localhost', 'port': 5432, 'username': 'admin', 'password': 'secret'}}{'host': 'localhost', 'port': 5432, 'username': 'admin', 'password': 'secret'}
    3server{'host': '0.0.0.0', 'port': 8080}{'database': {'host': 'localhost', 'port': 5432, 'username': 'admin', 'password': 'secret'}} {'database': {'host': 'localhost', 'port': 5432, 'username': 'admin', 'password': 'secret'}, 'server': {'host': '0.0.0.0', 'port': 8080}}{'host': '0.0.0.0', 'port': 8080}
  25. config.add_section("database", host="localhost", port=5432)

    183config = Config()184config⟨Config A⟩.add_section("database", host="localhost", port=5432)185config⟨Config A⟩.add_section("database", username="admin", password="secret")186config.add_section("server", host="0.0.0.0", port=8080)
  26. config.add_section("database", username="admin", password="secret")

    184config.add_section("database", host="localhost", port=5432)185config⟨Config A⟩.add_section("database", username="admin", password="secret")186config⟨Config A⟩.add_section("server", host="0.0.0.0", port=8080)
  27. config.add_section("server", host="0.0.0.0", port=8080)

    185config.add_section("database", username="admin", password="secret")186config⟨Config A⟩.add_section("server", host="0.0.0.0", port=8080)187188print("Final config:")189for section, settings in config.get().items():
    outputFinal config:
  28. def get(self):

    180def get(self⟨Config A⟩):181    return self.settings{'database': {'host': 'localhost', 'port': 5432, 'username': 'admin', 'password': 'secret'}, 'server': {'host': '0.0.0.0', 'port': 8080}}
  29. for section, settings in config.get().items():

    pass 1 of 2
    188print("Final config:")189for sectiondatabase, settings{'host': 'localhost', 'port': 5432, 'username': 'admin', 'password': 'secret'} in config⟨Config A⟩.get().items():190    print(f"  [{sectiondatabase}]")191    for key, value in settings.items():
    output  [database]
  30. for key, value in settings.items():

    pass 1 of 6
    190print(f"  [{section}]")191for keyhost, valuelocalhost in settings{'host': 'localhost', 'port': 5432, 'username': 'admin', 'password': 'secret'}.items():192    print(f"    {keyhost}={valuelocalhost}")
    output    host=localhost
    All 6 passes — pass 1 is the card above
    passkeyvaluesettingssectionconfig
    1hostlocalhost{'host': 'localhost', 'port': 5432, 'username': 'admin', 'password': 'secret'}
    2port5432{'host': 'localhost', 'port': 5432, 'username': 'admin', 'password': 'secret'}
    3usernameadmin{'host': 'localhost', 'port': 5432, 'username': 'admin', 'password': 'secret'}
    4passwordsecret{'host': 'localhost', 'port': 5432, 'username': 'admin', 'password': 'secret'}server⟨Config A⟩
    5host0.0.0.0{'host': '0.0.0.0', 'port': 8080}
    6port8080{'host': '0.0.0.0', 'port': 8080}
  31. for section, settings in config.get().items():

    pass 2 of 2
    188print("Final config:")189for sectionserver, settings{'host': '0.0.0.0', 'port': 8080} in config⟨Config A⟩.get().items():190    print(f"  [{sectionserver}]")191    for key, value in settings.items():
    output  [server]
  32. default_prefs ← {'theme': 'light', 'font_size': 12, 'auto_save': True, 'line_numbers': True}

    194# Merging user preferences195default_prefs→ {'theme': 'light', 'font_size': 12, 'auto_save': True, 'line_numbers': True} = {196    "theme": "light",197    "font_size": 12,198    "auto_save": True,199    "line_numbers": True200}201202user_prefs→ {'theme': 'dark', 'font_size': 14} = {203    "theme": "dark",204    "font_size": 14205}206207final_prefs→ {'theme': 'dark', 'font_size': 14, 'auto_save': True, 'line_numbers': True} = {**default_prefs{'theme': 'light', 'font_size': 12, 'auto_save': True, 'line_numbers': True}, **user_prefs{'theme': 'dark', 'font_size': 14}}208print(f"\nFinal preferences: {final_prefs{'theme': 'dark', 'font_size': 14, 'auto_save': True, 'line_numbers': True}}")
    output
    Final preferences: {'theme': 'dark', 'font_size': 14, 'auto_save': True, 'line_numbers': True}

Dictionary unpacking is the standard way to merge configurations, apply defaults, and build keyword argument sets.

dict unpacking Using `**` to merge dictionaries or pass dict contents as keyword arguments, with later values overwriting earlier ones.

Use Cases

  • Swap variables without a temporary: a, b = b, a
  • Extract function return values
  • Iterate over tuples with for x, y in pairs
  • Merge dictionaries: {**defaults, **overrides}
  • Forward function arguments with *args, **kwargs

Exercise: unpacking_practice.py

Parse a CSV line into name, age, and city variables, then merge two config dicts with unpacking