Python Specific
Unpacking
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.
This pattern is essential for building flexible APIs, decorators, and configuration systems.
Dictionary Unpacking
basic_unpacking.py
"""Basic unpacking examples"""
# Tuple unpacking
print("Tuple unpacking:")
point =
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 =
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 =
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}")
star_unpacking.py
"""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}")
nested_unpacking.py
"""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})")
function_args_unpacking.py
"""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}")
dict_unpacking.py
"""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}")
Dictionary unpacking is the standard way to merge configurations, apply defaults, and build keyword argument sets.
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
unpacking
Extracting values from a sequence into individual variables using assignment syntax like `a, b = [1, 2]`.
star unpacking
Using `*variable` to capture multiple remaining elements into a list, enabling flexible extraction from variable-length sequences.
nested unpacking
Unpacking multi-level data structures by matching the nesting pattern in the assignment target.
argument unpacking
Using `*` to expand sequences into positional arguments and `**` to expand dicts into keyword arguments when calling functions.
dict unpacking
Using `**` to merge dictionaries or pass dict contents as keyword arguments, with later values overwriting earlier ones.
Exercise: unpacking_practice.py
Parse a CSV line into name, age, and city variables, then merge two config dicts with unpacking