Python - Zip Two Lists Together
The `zip()` function takes two or more iterables and returns an iterator of tuples, where each tuple contains elements from the same position across all input iterables.
Key Insights
- Python’s
zip()function combines multiple iterables element-wise, creating tuples that pair corresponding elements from each input sequence - Zipping lists enables parallel iteration, dictionary creation from separate key-value lists, and elegant solutions for transposing data structures
- Understanding zip behavior with unequal-length lists and memory-efficient iteration patterns prevents common pitfalls in production code
Basic Zip Operations
The zip() function takes two or more iterables and returns an iterator of tuples, where each tuple contains elements from the same position across all input iterables.
names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 35]
zipped = zip(names, ages)
print(list(zipped))
# Output: [('Alice', 25), ('Bob', 30), ('Charlie', 35)]
The result is an iterator, not a list. Convert to a list when you need to reuse the data or inspect it during debugging. For one-time iteration, use the iterator directly to save memory:
names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 35]
for name, age in zip(names, ages):
print(f"{name} is {age} years old")
# Output:
# Alice is 25 years old
# Bob is 30 years old
# Charlie is 35 years old
Creating Dictionaries from Two Lists
One of the most practical applications is constructing dictionaries when you have separate lists for keys and values:
keys = ['username', 'email', 'role']
values = ['admin', 'admin@example.com', 'administrator']
user_dict = dict(zip(keys, values))
print(user_dict)
# Output: {'username': 'admin', 'email': 'admin@example.com', 'role': 'administrator'}
This pattern appears frequently when parsing configuration files or processing API responses where keys and values arrive separately:
# Simulating CSV header and row data
headers = ['id', 'product', 'price', 'quantity']
row = ['101', 'Laptop', '999.99', '5']
product = dict(zip(headers, row))
print(product)
# Output: {'id': '101', 'product': 'Laptop', 'price': '999.99', 'quantity': '5'}
Handling Unequal Length Lists
When lists have different lengths, zip() stops at the shortest list by default:
list1 = [1, 2, 3, 4, 5]
list2 = ['a', 'b', 'c']
result = list(zip(list1, list2))
print(result)
# Output: [(1, 'a'), (2, 'b'), (3, 'c')]
For Python 3.10+, use zip(strict=True) to raise an exception when lengths differ:
list1 = [1, 2, 3, 4, 5]
list2 = ['a', 'b', 'c']
try:
result = list(zip(list1, list2, strict=True))
except ValueError as e:
print(f"Error: {e}")
# Output: Error: zip() argument 2 is shorter than argument 1
For earlier Python versions or when you need all elements, use itertools.zip_longest():
from itertools import zip_longest
list1 = [1, 2, 3, 4, 5]
list2 = ['a', 'b', 'c']
result = list(zip_longest(list1, list2, fillvalue=None))
print(result)
# Output: [(1, 'a'), (2, 'b'), (3, 'c'), (4, None), (5, None)]
Specify a custom fill value for missing elements:
from itertools import zip_longest
prices = [10.99, 15.99, 20.99]
discounts = [0.1, 0.15]
result = list(zip_longest(prices, discounts, fillvalue=0.0))
print(result)
# Output: [(10.99, 0.1), (15.99, 0.15), (20.99, 0.0)]
Unzipping Lists
The zip() function is its own inverse when combined with the unpacking operator:
pairs = [('Alice', 25), ('Bob', 30), ('Charlie', 35)]
names, ages = zip(*pairs)
print(names) # Output: ('Alice', 'Bob', 'Charlie')
print(ages) # Output: (25, 30, 35)
This returns tuples, not lists. Convert explicitly if needed:
pairs = [('Alice', 25), ('Bob', 30), ('Charlie', 35)]
names, ages = map(list, zip(*pairs))
print(names) # Output: ['Alice', 'Bob', 'Charlie']
print(ages) # Output: [25, 30, 35]
Zipping Multiple Lists
zip() accepts any number of iterables:
ids = [1, 2, 3]
names = ['Alice', 'Bob', 'Charlie']
departments = ['Engineering', 'Sales', 'Marketing']
salaries = [120000, 80000, 95000]
employees = list(zip(ids, names, departments, salaries))
for emp in employees:
print(emp)
# Output:
# (1, 'Alice', 'Engineering', 120000)
# (2, 'Bob', 'Sales', 80000)
# (3, 'Charlie', 'Marketing', 95000)
Combine with dictionary comprehension for structured data:
ids = [1, 2, 3]
names = ['Alice', 'Bob', 'Charlie']
salaries = [120000, 80000, 95000]
employees = {
emp_id: {'name': name, 'salary': salary}
for emp_id, name, salary in zip(ids, names, salaries)
}
print(employees)
# Output: {1: {'name': 'Alice', 'salary': 120000},
# 2: {'name': 'Bob', 'salary': 80000},
# 3: {'name': 'Charlie', 'salary': 95000}}
Matrix Transposition
Transpose a matrix (list of lists) using zip with unpacking:
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
transposed = list(zip(*matrix))
print(transposed)
# Output: [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
Convert back to lists of lists:
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
transposed = [list(row) for row in zip(*matrix)]
print(transposed)
# Output: [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Parallel Iteration with Enumerate
Combine zip() with enumerate() for indexed parallel iteration:
questions = ['What is your name?', 'What is your quest?', 'What is your favorite color?']
answers = ['Arthur', 'To seek the Holy Grail', 'Blue']
for i, (question, answer) in enumerate(zip(questions, answers), start=1):
print(f"Q{i}: {question}")
print(f"A{i}: {answer}\n")
# Output:
# Q1: What is your name?
# A1: Arthur
#
# Q2: What is your quest?
# A2: To seek the Holy Grail
#
# Q3: What is your favorite color?
# A3: Blue
Aggregating Data from Multiple Sources
Process data from multiple lists simultaneously:
product_ids = ['P001', 'P002', 'P003']
quantities = [10, 5, 8]
unit_prices = [29.99, 49.99, 19.99]
total_value = sum(qty * price for qty, price in zip(quantities, unit_prices))
print(f"Total inventory value: ${total_value:.2f}")
# Output: Total inventory value: $659.75
# Detailed breakdown
for pid, qty, price in zip(product_ids, quantities, unit_prices):
line_total = qty * price
print(f"{pid}: {qty} units × ${price} = ${line_total:.2f}")
# Output:
# P001: 10 units × $29.99 = $299.90
# P002: 5 units × $49.99 = $249.95
# P003: 8 units × $19.99 = $159.92
Memory Efficiency Considerations
zip() returns an iterator that generates values on-demand. This matters for large datasets:
# Memory efficient - processes one pair at a time
large_list1 = range(1000000)
large_list2 = range(1000000)
for a, b in zip(large_list1, large_list2):
result = a + b
# Process without loading entire result set into memory
Avoid converting to list unless necessary:
# Inefficient - loads everything into memory
result = list(zip(range(1000000), range(1000000)))
# Efficient - iterate directly
for pair in zip(range(1000000), range(1000000)):
process(pair)
The zip() function provides elegant solutions for parallel data processing. Use it to combine related data, create dictionaries from separate key-value sources, transpose matrices, and iterate over multiple sequences simultaneously. Remember that zip() returns an iterator for memory efficiency and stops at the shortest input by default—use strict=True or zip_longest() when you need different behavior.