Skip to content
Home
Python File Handling: Read, Write, and Manage Files

Python File Handling: Read, Write, and Manage Files

Python Python 8 min read 1503 words Beginner ExcellentWiki Editorial Team

File handling is a fundamental skill in Python. Whether you are reading configuration files, writing logs, processing CSV exports, or working with binary data, Python’s built-in I/O tools make file operations consistent across platforms. The language’s philosophy of explicit is better than implicit is evident in how files are opened, processed, and closed — every operation is clear and predictable.

Opening Files

The built-in open() function is the entry point for all file operations:

file = open("data.txt", "r")
content = file.read()
file.close()

File Modes

ModeDescription
"r"Read (default)
"w"Write — truncates existing file
"a"Append — write to end
"x"Exclusive creation — fails if file exists
"r+"Read and write (no truncate)
"w+"Read and write (truncates)
"a+"Read and append
"rb" / "wb"Binary read / write

Context Managers (The Right Way)

Always use the with statement — it automatically closes the file, even if an exception occurs:

with open("data.txt", "r") as file:
    content = file.read()
# File is closed automatically here

The context manager is safer and more concise than manual close() calls. There is almost never a reason to call open() without with.

Reading Files

Read Entire File

with open("data.txt", "r") as f:
    content = f.read()
    print(len(content))  # total characters

For small files, read() is simple and fast. For large files, it loads everything into memory — avoid it for files over a few hundred megabytes.

Read Line by Line

with open("data.txt", "r") as f:
    for line in f:
        print(line.strip())  # strip removes trailing newline

Iterating over the file object reads one line at a time without loading the entire file. This is memory-efficient and is the recommended way to process text files.

Read All Lines into a List

with open("data.txt", "r") as f:
    lines = f.readlines()   # list of strings with newlines
    lines = f.read().splitlines()  # list without newlines

readlines() is convenient but loads the whole file. For large files, use the line-by-line iteration above.

Writing Files

Write Mode

with open("output.txt", "w") as f:
    f.write("Hello, world!\n")
    f.write("Second line\n")

"w" truncates the file if it exists. For appending, use "a".

Writing Multiple Lines

lines = ["first", "second", "third"]
with open("output.txt", "w") as f:
    for line in lines:
        f.write(line + "\n")

# Or use writelines (note: does NOT add newlines)
with open("output.txt", "w") as f:
    f.writelines(line + "\n" for line in lines)

Append Mode

with open("log.txt", "a") as f:
    f.write(f"{datetime.now()}: Operation completed\n")

Working with CSV Files

CSV is the most common data exchange format. Python’s csv module handles edge cases like quoted fields, embedded commas, and different delimiters:

import csv

# Reading CSV
with open("users.csv", "r") as f:
    reader = csv.DictReader(f)  # uses first row as fieldnames
    for row in reader:
        print(f"{row['name']} ({row['email']})")

# Reading CSV without header
with open("data.csv", "r") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)  # list of strings

# Writing CSV
with open("output.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["name", "email", "age"])
    writer.writerow(["Alice", "alice@example.com", 30])
    writer.writerow(["Bob", "bob@example.com", 25])

# Writing with DictWriter
with open("output.csv", "w", newline="") as f:
    fieldnames = ["name", "email", "age"]
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerow({"name": "Alice", "email": "alice@example.com", "age": 30})

The newline="" parameter in the writer is important — without it, CSV files on Windows may have extra blank lines.

Working with JSON Files

JSON is the standard for configuration and API data:

import json

# Writing JSON
data = {
    "name": "Alice",
    "age": 30,
    "skills": ["Python", "Go", "SQL"],
    "active": True
---

with open("data.json", "w") as f:
    json.dump(data, f, indent=2)  # pretty print

# Reading JSON
with open("data.json", "r") as f:
    loaded = json.load(f)
    print(loaded["name"])  # Alice

# JSON to string (not file)
json_str = json.dumps(data, indent=2)

# String to JSON
parsed = json.loads(json_str)

Handling Encoding

JSON only supports double-quoted strings and specific Unicode escapes. json.dumps with ensure_ascii=False preserves Unicode characters instead of escaping them:

data = {"name": "José"}
print(json.dumps(data, ensure_ascii=False))  # {"name": "José"}

Working with Binary Files

Binary mode is essential for images, audio, archives, and serialized data:

# Copy a binary file in chunks
with open("image.jpg", "rb") as src, open("copy.jpg", "wb") as dst:
    while chunk := src.read(8192):  # 8 KB chunks
        dst.write(chunk)

# Read binary data
with open("file.bin", "rb") as f:
    header = f.read(4)  # first 4 bytes
    f.seek(100)         # seek to position 100
    data = f.read(16)   # read 16 bytes from there

The walrus operator := with chunk := src.read(8192) reads until the chunk is empty (EOF). This is the idiomatic way to copy binary streams.

Working with Paths

Python 3.4+ has pathlib for modern path handling:

from pathlib import Path

path = Path("data/settings.json")

# Check existence
print(path.exists())     # True/False
print(path.is_file())    # True/False
print(path.is_dir())     # True/False

# Read/write with pathlib
content = path.read_text()       # equivalent to open + read
path.write_text("new content")   # equivalent to open + write
path.read_bytes()                # binary version

# Directory operations
for child in Path("data").iterdir():
    print(child.name)

# Glob patterns
for py_file in Path("src").glob("**/*.py"):
    print(py_file)

# Create directories
Path("output/logs/2024").mkdir(parents=True, exist_ok=True)

File and Directory Operations

import os
import shutil

# Rename or move
os.rename("old.txt", "new.txt")
shutil.move("src.txt", "backup/src.txt")

# Copy
shutil.copy("source.txt", "dest.txt")
shutil.copytree("src_dir", "dst_dir")  # recursive copy

# Delete
os.remove("file.txt")       # delete file
os.rmdir("empty_dir")       # delete empty directory
shutil.rmtree("dir")        # delete directory and contents

# List directory
for entry in os.listdir("."):
    print(entry)

# Walk recursively
for dirpath, dirnames, filenames in os.walk("src"):
    for filename in filenames:
        print(os.path.join(dirpath, filename))

Best Practices

Always use context managerswith open(...) ensures files are closed even on exceptions. There is no excuse for bare open() calls.

Specify encoding explicitly — Python’s default encoding varies by platform. Always pass encoding="utf-8" for text files:

with open("data.txt", "r", encoding="utf-8") as f:
    content = f.read()

Use newline="" with csv module — Prevents platform-specific newline issues.

Process large files line by line — Never call read() or readlines() on files larger than available memory.

Prefer pathlib over os.pathpathlib paths are objects with methods, not strings with functions. They compose better and are cross-platform.


Related: Learn about Python error handling and list comprehensions.

Working with Binary Files

Python’s file handling extends beyond text to binary data. Open files with 'rb' or 'wb' mode to read and write bytes. The struct module packs and unpacks binary data according to format strings, essential for working with binary file formats, network protocols, and serialized data. Memory-mapped files via mmap allow treating file contents as a mutable byte array, providing efficient random access for large files without loading them entirely into memory.

File Locking

When multiple processes access the same file, locking prevents corruption. The fcntl module provides POSIX file locking on Unix systems. For cross-platform locking, the portalocker library provides a consistent API. Lock files (.lock files) serve as a simple advisory locking mechanism: create the lock file atomically and delete it when done.

Practical Applications and Real-World Usage

Python’s versatility makes it applicable across virtually every domain in software development. In data science, Python powers the machine learning ecosystem with libraries like scikit-learn for classical ML, TensorFlow and PyTorch for deep learning, and pandas for data manipulation. In web development, Django provides a full-featured framework for content-heavy sites while FastAPI delivers high-performance async APIs. In DevOps, Python scripts automate infrastructure, manage cloud resources, and orchestrate CI/CD pipelines. System administrators use Python for log analysis, network monitoring, and configuration management. The language’s extensive standard library — often described as “batteries included” — provides modules for JSON/XML parsing, CSV handling, email processing, compression, cryptography, and concurrent programming without requiring any third-party packages. This breadth of applicability means learning Python opens doors to multiple career paths, and the skills transfer readily between domains. The Python Package Index (PyPI) hosts over 500,000 packages, meaning virtually any task you encounter has a well-maintained library available. When evaluating libraries, check GitHub stars, maintenance frequency, documentation quality, and community size. Popular well-maintained libraries with active communities are usually safer choices than newer alternatives with fewer users, unless the newer library addresses a specific need the established one does not.

Performance Optimization Strategies

While Python is not the fastest language, many optimization strategies can dramatically improve performance. Use built-in functions and standard library modules — they are implemented in C and run orders of magnitude faster than equivalent Python loops. Profile before optimizing: use cProfile to identify actual bottlenecks rather than guessing. For CPU-bound tasks, consider NumPy (vectorized operations), Cython (compile Python to C), or Numba (JIT compilation). For parallel processing, the multiprocessing module bypasses the GIL by using separate processes. For I/O-bound tasks, asyncio provides cooperative multitasking with minimal overhead. Remember the 80/20 rule: 80% of execution time is typically spent in 20% of the code. Optimize that 20% and leave the rest readable.

FAQ

What is the most important thing to remember? Focus on understanding the core concepts thoroughly before moving to advanced topics. Mastery comes from practice, not just reading.

How long does it take to learn this? The timeline varies by individual, but consistent daily practice of 30-60 minutes yields visible progress within weeks.

What are common mistakes beginners make? The most frequent errors include skipping fundamentals, not testing assumptions, and trying to learn too many things simultaneously.

Section: Python 1503 words 8 min read Beginner 756 articles in section Report inaccuracy Back to top