The Python zipfile module works with .zip archive files on disk or in memory: create archives, add files, list members, read contents, and extract. It is not the built-in zip() function, which pairs items from iterables into tuples. The similar names confuse beginners—remember zipfile = archive files, zip() = combine lists.
Tested on: Python 3.13.3; kernel 6.14.0-37-generic.
Python zipfile quick reference
| Task | Method / class |
|---|---|
| Open a ZIP archive | ZipFile() |
| Create a new ZIP file | ZipFile(..., 'w') |
| Add a file | write() |
| Add text or bytes as a file | writestr() |
| List file names | namelist() |
| List file metadata | infolist() |
| Read a file without extracting | read() or open() |
| Extract one file | extract() |
| Extract all files | extractall() |
| Test archive integrity | testzip() |
| Read metadata for one file | getinfo() |
| Work with large ZIP files | allowZip64=True |
zipfile vs zip(): what is the difference?
Built-in zip() |
zipfile module |
|
|---|---|---|
| Purpose | Pair iterables by index | Read/write .zip archives |
| Import | Always available | import zipfile |
| Typical output | Iterator of tuples | Files on disk or bytes in memory |
The built-in zip() pairs iterables; see that article for strict=True and zip_longest() when you need tuple-wise iteration instead of archive I/O.
Import Python zipfile module
import zipfile
from zipfile import ZipFileNo pip install is required—zipfile ships with Python. The ZipFile class is the main entry point; use it as a context manager (with ZipFile(...) as zf:) so the archive closes reliably.
Create a ZIP file in Python
Open ZipFile in write mode ('w'). That creates a new archive or overwrites an existing file at the same path. Confirm the destination path with check if a file exists when you must avoid clobbering an unrelated file at the same name.
import tempfile
import zipfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmp:
src = Path(tmp) / "hello.txt"
src.write_text("Hello ZIP\n")
zip_path = Path(tmp) / "demo.zip"
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
zf.write(src, arcname="hello.txt")
zf.writestr("notes.txt", "Inline text\n")
with zipfile.ZipFile(zip_path) as zf:
print(zf.namelist())You should see ['hello.txt', 'notes.txt']. The arcname argument controls the path inside the archive; without it, write() uses the source file’s basename.
writestr() adds a member from a str or bytes value when you do not have a separate file on disk.
Add files to an existing ZIP file
Use append mode ('a') to add entries to an existing archive without rebuilding the whole file:
import tempfile
import zipfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmp:
zip_path = Path(tmp) / "demo.zip"
with zipfile.ZipFile(zip_path, "w") as zf:
zf.writestr("a.txt", "first")
with zipfile.ZipFile(zip_path, "a") as zf:
zf.writestr("b.txt", "second")
with zipfile.ZipFile(zip_path) as zf:
print(zf.namelist())You should see both names. Append adds new entries; it does not remove old ones. If you writestr() or write() with an arcname that already exists, you can end up with duplicate member names in one archive—avoid reusing the same internal path unless you intend to replace via a rebuild.
List files inside a ZIP archive
import tempfile
import zipfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmp:
zip_path = Path(tmp) / "demo.zip"
with zipfile.ZipFile(zip_path, "w") as zf:
zf.writestr("data/report.csv", "a,b\n1,2\n")
with zipfile.ZipFile(zip_path) as zf:
print(zf.namelist())
for info in zf.infolist():
print(info.filename, info.file_size, info.compress_size)
one = zf.getinfo("data/report.csv")
print(one.filename, one.file_size)namelist()— member paths as stringsinfolist()—ZipInfoobjects (size, compression, date, etc.)getinfo(name)— metadata for one member (raisesKeyErrorif missing)
printdir() prints a human-readable listing when you are exploring interactively.
Read files from ZIP without extracting
read(name) returns the member as bytes. open(name) returns a file-like object—better for large files or line-by-line reads:
import tempfile
import zipfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmp:
zip_path = Path(tmp) / "demo.zip"
with zipfile.ZipFile(zip_path, "w") as zf:
zf.writestr("hello.txt", "Hello ZIP\n")
with zipfile.ZipFile(zip_path) as zf:
print(zf.read("hello.txt"))
with zf.open("hello.txt") as f:
print(f.read())Both calls return b'Hello ZIP\n'. Decode with .decode() when you expect text. Prefer open() when the member is large so you stream instead of loading everything into memory.
Extract ZIP files in Python
import tempfile
import zipfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmp:
zip_path = Path(tmp) / "demo.zip"
out_dir = Path(tmp) / "out"
out_dir.mkdir()
with zipfile.ZipFile(zip_path, "w") as zf:
zf.writestr("a.txt", "A\n")
zf.writestr("b.txt", "B\n")
with zipfile.ZipFile(zip_path) as zf:
zf.extract("a.txt", path=out_dir)
zf.extractall(path=out_dir)
print(sorted(p.name for p in out_dir.iterdir()))extract(member, path=...) pulls one file. extractall(path=...) unpacks every member. Pass a directory to path to control the destination.
Extract only selected members by looping:
with zipfile.ZipFile(zip_path) as zf:
for name in zf.namelist():
if name.endswith(".txt"):
zf.extract(name, path=out_dir)Handle password-protected ZIP files
The standard zipfile module can read many encrypted ZIP files. Pass the password as bytes to pwd= on extract(), extractall(), read(), or open(), or set a default with setpassword():
with zipfile.ZipFile("secret.zip") as zf:
zf.setpassword(b"my-password")
data = zf.read("confidential.txt")Encryption support depends on how the archive was created (classic ZipCrypto vs AES). Unsupported schemes raise RuntimeError or NotImplementedError.
zip on the command line) or a third-party library—not ZipFile.write() alone.
Choose compression method and compression level
| Constant | Meaning |
|---|---|
ZIP_STORED |
No compression—fast, larger files |
ZIP_DEFLATED |
Default choice—DEFLATE compression (widely compatible) |
ZIP_BZIP2 |
BZIP2—often smaller, slower; needs bz2 support |
ZIP_LZMA |
LZMA—strong compression; needs lzma support |
Pass compression= when opening in write mode:
with zipfile.ZipFile("out.zip", "w", compression=zipfile.ZIP_DEFLATED, compresslevel=6) as zf:
zf.writestr("data.txt", "payload")compresslevel (0–9 for ZIP_DEFLATED) trades speed vs size. Use ZIP_STORED when data is already compressed (JPEG, PNG, gzip). Exotic methods may produce archives that older unzip tools cannot read.
Create ZIP files in memory with BytesIO
For web downloads, APIs, or temporary reports, build the archive in memory:
import zipfile
from io import BytesIO
buffer = BytesIO()
with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as zf:
zf.writestr("report.txt", "Generated content\n")
payload = buffer.getvalue()
print(len(payload))You should see a byte length greater than zero. Send payload in an HTTP response, or persist it with Path("out.zip").write_bytes(payload)—the same path and mode ideas as write to file in Python.
Work with large ZIP files safely
- Prefer
ZipFile.open(name)overread(name)when members are large—read in chunks or line by line. - Keep
allowZip64=True(the default) so archives can exceed 4 GB and hold large files. - Avoid loading every member into memory at once when processing big archives.
- Call
testzip()to find the first corrupt member (Nonemeans no bad CRC detected in the test pass):
with zipfile.ZipFile("big.zip") as zf:
bad = zf.testzip()
print(bad)Handle zipfile errors
| Exception | Typical cause |
|---|---|
BadZipFile |
File is missing, truncated, or not a valid ZIP |
LargeZipFile |
Archive needs ZIP64 but allowZip64=False |
KeyError |
Member name not in the archive (read / getinfo) |
RuntimeError / NotImplementedError |
Unsupported compression or encryption method |
FileNotFoundError |
Archive path does not exist when opening |
import zipfile
from pathlib import Path
path = Path("not-a-zip.bin")
path.write_bytes(b"not a zip")
try:
zipfile.ZipFile(path)
except zipfile.BadZipFile:
print("invalid archive")Wrap production code in try/except when paths come from users or downloads.
Can you delete a file from a ZIP archive?
There is no delete() method on ZipFile. To remove one member, write a new archive and copy everything except the unwanted entry:
import tempfile
import zipfile
from pathlib import Path
def rebuild_without(zip_in: Path, zip_out: Path, skip: str) -> None:
with zipfile.ZipFile(zip_in, "r") as zin, zipfile.ZipFile(
zip_out, "w", zipfile.ZIP_DEFLATED
) as zout:
for info in zin.infolist():
if info.filename == skip:
continue
zout.writestr(info, zin.read(info.filename))
with tempfile.TemporaryDirectory() as tmp:
src = Path(tmp) / "old.zip"
dst = Path(tmp) / "new.zip"
with zipfile.ZipFile(src, "w") as zf:
zf.writestr("keep.txt", "stay")
zf.writestr("drop.txt", "go")
rebuild_without(src, dst, "drop.txt")
with zipfile.ZipFile(dst) as zf:
print(zf.namelist())You should see only keep.txt. Replace the original file only after the new archive verifies correctly.
Summary
The Python zipfile module manages .zip archives—separate from built-in zip(). Use ZipFile in 'w' to create (overwriting an existing path), 'a' to append members, and 'r' to read. Common methods: write(), writestr(), namelist(), read(), open(), extract(), and extractall(). Prefer open() for large members; use allowZip64=True for big archives. The module can extract many password-protected ZIPs with pwd= or setpassword() but cannot create encrypted archives. To drop a member, rebuild a new ZIP without that entry.

