Python zip() Function

Learn how the Python zip() function works with lists, tuples, dictionaries, and loops. See how to handle different lengths with strict=True and zip_longest().

Published

Updated

Read time 8 min read

Reviewed byDeepak Prasad

Python zip() Function

The Python zip() function combines items from two or more iterables position by position: first with first, second with second, and so on. Each step produces a tuple. The return value is a zip object—an iterator—not a list, so you usually wrap it with list() or loop over it with for. By default, zip() stops when the shortest iterable runs out, which is easy to forget when lengths do not match.

This guide covers the zip() function in Python with practical examples: quick reference, syntax, converting results, looping, unequal lengths, strict=True, zip_longest(), dictionaries, unzipping, and common mistakes. For walking two lists in a loop with more recipes, see loop through two lists in Python. Built-in zip() is not the zipfile module for archive files.

Tested on: Python 3.13.3; kernel 6.14.0-37-generic.


Python zip() quick reference

Task Use
Pair two lists item by item zip(list1, list2)
Pair three or more iterables zip(a, b, c)
See the output as a list list(zip(...))
Loop through two lists together for x, y in zip(a, b):
Create a dictionary from keys and values dict(zip(keys, values))
Split zipped pairs back into separate values zip(*pairs)
Stop silently at the shortest iterable zip(a, b)
Raise an error if lengths differ zip(a, b, strict=True)
Keep all values from the longest iterable itertools.zip_longest()

Use normal zip() when unmatched values can be ignored. Use strict=True when different lengths indicate a bug. Use zip_longest() when missing values must be filled instead of dropped.


What does zip() do in Python?

zip() walks multiple iterables in lockstep. The first item from each input forms the first tuple, the second items form the second tuple, and so on. It works with lists, tuples, strings, ranges, and other iterables. In Python 3, the result is a lazy iterator (a zip object), not a materialized list.

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

paired = zip(names, scores)
print(list(paired))
Output

You should see [('Alice', 85), ('Bob', 92), ('Charlie', 77)]. Each name is paired with the score at the same index—no manual range(len(...)) required.

With a single iterable, each tuple holds one element: list(zip([1, 2, 3]))[(1,), (2,), (3,)].


Python zip() syntax

python
zip(*iterables, strict=False)
Output
  • *iterables — pass one, two, three, or more iterables. The star means “any number of positional iterables” (same idea as *args in function definitions, but here it is part of zip’s call signature).
  • strict=False — default. Pair until the shortest iterable ends.
  • strict=True — Python 3.10+. Raise ValueError if any iterable is longer or shorter than the others.

See the built-in zip() docs for the full signature.


Convert a zip object to a list, tuple, or dictionary

Printing zip(...) alone shows something like <zip object at 0x...>. Convert when you need to inspect or reuse the full result:

python
names = ["Alice", "Bob"]
scores = [85, 92]

print(list(zip(names, scores)))
print(tuple(zip(names, scores)))

keys = ["name", "age", "city"]
values = ["Alice", 30, "London"]
print(dict(zip(keys, values)))
Output

You should see [('Alice', 85), ('Bob', 92)], the same pairs as a tuple, then {'name': 'Alice', 'age': 30, 'city': 'London'}.

A zip object is consumed after one full iteration. If you need the pairs twice, store pairs = list(zip(a, b)) first:

python
z = zip(names, scores)
print(list(z))
print(list(z))
Output

You should see the pairs once, then an empty list [] on the second pass.


Loop through two or more lists with zip()

This is one of the most practical uses of the Python zip() function. It avoids index arithmetic and reads clearly when two lists are related by position. The same pattern works inside a for loop over any parallel iterables.

Two lists:

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

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

Three lists:

python
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 77]
grades = ["B", "A", "A"]

for name, score, grade in zip(names, scores, grades):
    print(name, score, grade)
Output

You should see three aligned rows. More patterns (indexes with enumerate, unequal lengths, building dicts) live in loop through two lists in Python.


What happens when zip() gets lists of different lengths?

By default, zip() stops when the shortest iterable is exhausted. Extra values in longer iterables are ignored—not an error.

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

print(list(zip(names, scores)))
Output

You should see [('Alice', 85), ('Bob', 92)]. Charlie has no score in the output.

That truncation is fine when you intentionally take the minimum length. It is dangerous when you expected every row to align—silent data loss.

WARNING
If different lengths mean bad data in your program, do not rely on plain zip(). Use strict=True to catch the mismatch, or zip_longest() when you need to keep every value.

Use strict=True when different lengths should be an error

strict=True (Python 3.10+) raises ValueError when iterables differ in length. Use it when lists must match exactly: IDs and names, CSV columns, test fixtures, parallel record fields.

python
ids = [1, 2, 3]
names = ["Alice", "Bob"]

try:
    list(zip(ids, names, strict=True))
except ValueError as e:
    print(e)
Output

You should see a message that one argument is shorter than the other. That fails fast instead of dropping 3.

When lengths are guaranteed equal, strict=True documents intent and catches bugs early. It does not pad or keep extras—use zip_longest() for that.


Use itertools.zip_longest() to keep unmatched values

itertools.zip_longest() continues until the longest iterable ends. Shorter inputs are padded with fillvalue (default None):

python
from itertools import zip_longest

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

print(list(zip_longest(names, scores, fillvalue="N/A")))
Output

You should see [('Alice', 85), ('Bob', 92), ('Charlie', 'N/A')]. The third name stays; the missing score is filled.

Situation Best choice
Ignore extras from longer iterables zip()
Treat unequal lengths as a bug zip(..., strict=True)
Keep all values and fill missing items zip_longest()

Import with from itertools import zip_longest or import itertools and call itertools.zip_longest(...).


Create a dictionary with zip()

dict(zip(keys, values)) is a common Python pattern: the first iterable supplies keys, the second supplies values, paired by index.

python
keys = ["name", "age", "city"]
values = ["Alice", 30, "London"]

person = dict(zip(keys, values))
print(person)
Output

You should see {'name': 'Alice', 'age': 30, 'city': 'London'}. See Python dictionary for more on dicts.

If lengths differ, normal zip() still stops at the shortest pair— you may get fewer keys than expected. Validate first when missing entries should fail:

python
dict(zip(keys, values, strict=True))
Output

Unzip a zipped object in Python

Python has no separate unzip() function. Use zip(*pairs) to split a sequence of tuples back into columns:

python
pairs = [("Alice", 90), ("Bob", 85), ("Charlie", 78)]
names, scores = zip(*pairs)

print(list(names))
print(list(scores))
Output

You should see ['Alice', 'Bob', 'Charlie'] and [90, 85, 78]. The * unpacks each tuple as a separate argument to zip().

Results are tuples; wrap with list() when you need lists. Unzipping consumes a one-pass iterator—materialize list(pairs) first if you need the original pairs again.


Use zip() with strings, tuples, ranges, and dictionaries

Stringszip() pairs characters by position: list(zip("ab", "12"))[('a', '1'), ('b', '2')].

Tuples — same behavior as lists.

range() — pair generated numbers with another sequence: list(zip(range(3), ["a", "b", "c"])). See Python range() for start, stop, and step rules.

Dictionaries — iterating a dict yields keys by default. To pair keys with values, dict.items() is usually clearer than zip(d, d.values()):

python
person = {"name": "Alice", "age": 30}
print(list(person.items()))
Output

Setszip() accepts sets, but sets are unordered. Do not rely on stable pairing when order matters.


zip() vs enumerate() vs map() vs zip_longest()

Function Use when
zip() You need items from multiple iterables at the same position
enumerate() You need index + item from one iterable
map() You want to apply a function to items from one or more iterables
zip_longest() You want to pair unequal-length iterables without dropping extra values

zip() aligns parallel columns. enumerate() adds a counter to a single sequence. map() transforms; it can take multiple iterables like zip, but the goal is applying a function, not just pairing.


Practical scenarios where zip() is useful

Pair names with scoresfor name, score in zip(names, scores): for reports or formatted output.

Build a dictionary from two listsdict(zip(keys, values)) for config maps and labeled records.

Process two columns together — sum, multiply, or merge aligned fields: [a + b for a, b in zip(col_a, col_b)].

Compare expected and actualfor exp, got in zip(expected, actual): to spot mismatches row by row (use strict=True when lengths must match).

Combine multiple lists in one loopzip(a, b, c) for three parallel sequences without nested indexing.

Transpose rows and columnslist(zip(*matrix)) turns row tuples into column tuples.

Unzip paired datanames, scores = zip(*pairs) to split CSV-like tuples into separate sequences.


Mistakes to avoid with zip()

  • Expecting zip() to return a list—it returns an iterator; use list(zip(...)) when you need all pairs at once.
  • Iterating the same zip object twice without saving list(...) first—the second pass is empty.
  • Losing trailing data when lengths differ and not noticing truncation.
  • Using plain zip() when strict=True would catch a length bug in IDs, keys, or columns.
  • Zipping sets when order must be stable.
  • Building dict(zip(keys, values)) from unequal lists without checking length—silent missing keys or values.

Summary

  • zip() pairs iterables by position and returns an iterator of tuples.
  • Wrap with list(), tuple(), or dict(zip(keys, values)) when you need a concrete collection.
  • By default, it stops at the shortest iterable—extras are dropped silently.
  • Use strict=True when unequal length means bad data (Python 3.10+).
  • Use itertools.zip_longest() when you must keep every value and pad shorter sequences.
  • Unzip with zip(*pairs); for archive files, use zipfile, not built-in zip().

Frequently Asked Questions

1. What does Python zip() return?

An iterator of tuples (a zip object in Python 3), not a list. Wrap with list() or loop with for to consume the pairs.

2. What happens when zipped lists have different lengths?

zip() stops at the shortest iterable and drops extra items. Use strict=True to raise ValueError, or itertools.zip_longest() to pad shorter iterables.

3. How do you unzip tuples in Python?

There is no unzip() function—use zip(*pairs) to split a sequence of tuples into separate columns, then list() if you need lists.

4. Can zip() build a dictionary from two lists?

Yes—dict(zip(keys, values)) pairs by index. Unequal lengths follow normal zip truncation unless you validate with strict=True first.

5. Is zip() the same as the zipfile module?

No—built-in zip() combines iterables into tuples. The zipfile module reads and writes .zip archive files on disk.
Bashir Alam

Data Analyst and Machine Learning Engineer

Computer Science graduate from the University of Central Asia, currently employed as a full-time Machine Learning Engineer at uExel. His expertise lies in OCR, text extraction, data preprocessing, and …