Skip to main content
Python•Last Updated : 20 Oct 2026
~45 min read

Files and Modules in Python

Reading and writing text files safely with with, working with CSV and JSON, handling paths, importing from the standard library, and splitting your own program into modules.

Why programs need files

Everything a program keeps in variables disappears when it ends. To remember data between runs (a to-do list, settings, scores) or to process data someone else produced, a program reads and writes files.

The built-in open(path, mode) returns a file object. The mode says what you intend to do:

  • "r": read (the default); the file must exist.
  • "w": write; creates the file, or empties an existing one first.
  • "a": append; writes at the end and keeps what is there.

Always open files inside a with statement. It closes the file automatically when the block ends, even if an exception occurs, so data is flushed to disk and the file is released. Also pass encoding="utf-8" for text, so the program behaves the same on every operating system.

Caution

Opening an existing file with "w" erases it immediately, before you write anything. Use "a" to add to the end.

PythonWrite a file, then read it back
1with open("notes.txt", "w", encoding="utf-8") as f:
2 f.write("Buy milk\n")
3 f.write("Call Ada\n")
4
5with open("notes.txt", encoding="utf-8") as f:
6 content = f.read()
7print(content)
8print(repr(content))
Output
Buy milk
Call Ada

'Buy milk\nCall Ada\n'

Reading a file line by line

f.read() returns the whole file as one string, which is fine for small files. For anything that might be large, loop over the file object itself: it yields one line at a time, so memory use stays small however big the file is.

Each line keeps its newline character at the end. line.rstrip("\n") (or line.strip() when surrounding spaces do not matter) removes it before you process the text. f.readlines() returns all lines as a list when you really need them all at once.

PythonProcessing lines
1with open("scores.txt", "w", encoding="utf-8") as f:
2 f.write("ada 91\ngrace 88\n\nlinus 79\n")
3
4total = 0
5with open("scores.txt", encoding="utf-8") as f:
6 for number, line in enumerate(f, start=1):
7 line = line.strip()
8 if not line:
9 continue
10 name, score = line.split()
11 total += int(score)
12 print(number, name.title(), score)
13print("Total:", total)
Output
1 Ada 91
2 Grace 88
4 Linus 79
Total: 258

Writing and appending

f.write(text) writes exactly the text you give it and returns the number of characters written. It does not add a newline, so include \n yourself. print(..., file=f) is a convenient alternative that adds the newline and the spaces between values for you.

Append mode is the natural choice for logs: every run adds lines and nothing is lost.

PythonAppending to a log
1for run in range(1, 4):
2 with open("log.txt", "a", encoding="utf-8") as log:
3 print("run", run, "finished", file=log)
4
5with open("log.txt", encoding="utf-8") as log:
6 lines = log.readlines()
7print(len(lines), "lines")
8print(lines[-1].rstrip())
Output
3 lines
run 3 finished

CSV files with the csv module

CSV (comma-separated values) is the most common format for tables: one record per line, fields separated by commas. Splitting on commas yourself breaks as soon as a field contains a comma, which CSV handles by quoting the field ("Lovelace, Ada"). The standard csv module does the quoting and unquoting correctly.

  • csv.writer(f).writerow([...]) writes a row; csv.DictWriter writes dictionaries.
  • csv.reader(f) yields each row as a list of strings; csv.DictReader(f) yields dictionaries keyed by the header line.

Open CSV files with newline="" as the csv documentation recommends, and remember that every field you read is a string: convert numbers yourself.

PythonWriting and reading CSV
1import csv
2
3rows = [["name", "score"], ["Lovelace, Ada", 91], ["Hopper, Grace", 88]]
4with open("grades.csv", "w", newline="", encoding="utf-8") as f:
5 csv.writer(f).writerows(rows)
6
7with open("grades.csv", encoding="utf-8") as f:
8 print(f.read().strip())
9
10with open("grades.csv", newline="", encoding="utf-8") as f:
11 for row in csv.DictReader(f):
12 print(row["name"], "->", int(row["score"]) + 5)
Output
name,score
"Lovelace, Ada",91
"Hopper, Grace",88
Lovelace, Ada -> 96
Hopper, Grace -> 93

JSON: saving structured data

JSON stores nested data (dictionaries, lists, strings, numbers, booleans and null) as text. It is the format of most web APIs and a simple way to save a program's state.

  • json.dump(data, f) writes Python data to a file; json.load(f) reads it back.
  • json.dumps(data) and json.loads(text) do the same with strings.

Python's True, False and None become true, false and null. Tuples become lists, and dictionary keys always become strings. indent=2 makes the file readable for people.

PythonSaving and loading state
1import json
2
3state = {"player": "Ada", "level": 3, "inventory": ["map", "key"], "boss_defeated": False}
4with open("save.json", "w", encoding="utf-8") as f:
5 json.dump(state, f, indent=2)
6
7with open("save.json", encoding="utf-8") as f:
8 loaded = json.load(f)
9print(loaded == state)
10print(loaded["inventory"][1])
11print(json.dumps({"ok": True, "score": None, 1: (2, 3)}))
Output
True
key
{"ok": true, "score": null, "1": [2, 3]}

Paths and missing files

The pathlib module represents file paths as objects: Path("data") / "report.csv" joins parts with the right separator for the operating system, and methods such as exists(), suffix, stem, read_text() and write_text() cover the common cases in one call.

A relative path such as "notes.txt" is resolved from the current working directory, the folder the program was started from, which is not necessarily the folder the script lives in. When a program cannot find its data file, this is usually why.

Opening a file that does not exist raises FileNotFoundError. Handle it where a sensible fallback exists, for example starting with an empty list the first time a program runs.

Pythonpathlib and a first-run fallback
1import json
2from pathlib import Path
3
4report = Path("data") / "report.csv"
5print(report.name, report.suffix, report.stem)
6
7def load_tasks(path):
8 try:
9 with open(path, encoding="utf-8") as f:
10 return json.load(f)
11 except FileNotFoundError:
12 return []
13
14print(load_tasks("tasks.json"))
15Path("tasks.json").write_text('["learn files"]', encoding="utf-8")
16print(load_tasks("tasks.json"))
17print(Path("tasks.json").exists())
Output
report.csv .csv report
[]
['learn files']
True

Modules and the standard library

A module is a file of Python code that other code can import. Python ships with a large standard library of modules, so many problems are already solved before you install anything.

There are three common import forms:

  • import math then math.sqrt(2): the module name keeps it clear where a name comes from.
  • from math import sqrt then sqrt(2): shorter, for names you use often.
  • import statistics as st: an alias for long module names.

Avoid from module import *: it dumps every name into your file and makes it unclear where anything came from.

Standard library modules worth knowing early

ModuleUse it for
mathsqrt, floor, ceil, pi, gcd
randomrandom numbers, choice, shuffle (seed it for repeatable results)
datetimedates, times, durations, formatting
collectionsCounter, defaultdict, deque, namedtuple
statisticsmean, median, mode, stdev
csv / jsonreading and writing data files
pathlib / ospaths, folders, environment
PythonA tour of a few modules
1import statistics as st
2from collections import Counter
3from datetime import date, timedelta
4
5scores = [72, 95, 88, 61, 88]
6print(st.mean(scores), st.median(scores), st.mode(scores))
7
8start = date(2026, 10, 20)
9deadline = start + timedelta(days=30)
10print(deadline.isoformat(), deadline.strftime("%A"))
11
12print(Counter("mississippi").most_common(2))
Output
80.8 88 88
2026-11-19 Thursday
[('i', 4), ('s', 4)]

Splitting your own program into modules

Any .py file is a module. As a program grows, move related functions into their own file and import them where they are needed. A typical small project looks like this:

expenses/
    main.py        # menu and input/output
    storage.py     # load and save the data file
    reports.py     # calculations and summaries
# reports.py
def total(expenses):
    return sum(e["amount"] for e in expenses)
# main.py
from reports import total
import storage

expenses = storage.load("expenses.json")
print("Spent so far:", total(expenses))

Each file now has one responsibility, can be read on its own, and can be tested without running the whole program. Python finds reports.py because it sits in the same folder as the script being run.

if __name__ == "__main__"

When a module is imported, Python runs all of its top-level code. That is a problem if the file also contains a demo or a menu loop that should only run when the file is started directly.

Python sets the special variable __name__ to "__main__" for the file you run, and to the module's own name when it is imported. Putting the start-up code under if __name__ == "__main__": makes a file usable both as a program and as an importable module. Wrapping that code in a main() function keeps its variables out of the module's global names.

PythonThe main guard
1def greet(name):
2 return f"Hello, {name}!"
3
4def main():
5 print(greet("world"))
6
7print("__name__ is", __name__)
8if __name__ == "__main__":
9 main()
Output
__name__ is __main__
Hello, world!

Installing third-party packages

Beyond the standard library, thousands of packages are published on the Python Package Index (PyPI) and installed with pip: python -m pip install requests. Install them inside a virtual environment so each project has its own versions:

python -m venv .venv
.venv\Scripts\activate      (Windows)
source .venv/bin/activate   (macOS / Linux)
python -m pip install requests

Record what a project needs in a requirements.txt file (python -m pip freeze > requirements.txt) so someone else can recreate the same environment. Everything in this course uses only the standard library, so no installation is needed for the exercises.

Key Takeaways & Summary
  • •Open files with with and an explicit encoding; the file closes itself.
  • •"w" empties a file, "a" appends, "r" reads.
  • •Use csv and json instead of parsing those formats by hand.
  • •Split programs into modules with one responsibility each, and guard start-up code with if __name__ == "__main__".

Check your understanding

Suggested Quiz
Question 1 of 5

What happens when you open an existing file with mode "w"?

Practice

Read quoted CSV with the csv module, group a sales export like a data-analysis library would, and parse an HTML page with the standard library's html.parser.

[Problem]

CSV Grade Report

Reveal Solution & Analysis
Model Solution
[Problem]

Pandas-Style Sales Analysis

Reveal Solution & Analysis
Model Solution
[Problem]

Web Scraper: Extract Article Links

Reveal Solution & Analysis
Model Solution

Quick Syntax & Reference Sheet

Concept / RuleSyntax / Usage
Read a whole filewith open(p, encoding="utf-8") as f: text = f.read()
Read line by linefor line in f: line = line.rstrip("\n")
Write / appendopen(p, "w") / open(p, "a")
CSV rows as dictsfor row in csv.DictReader(f):
Save / load JSONjson.dump(data, f, indent=2); data = json.load(f)
Join pathsPath("data") / "report.csv"
Import formsimport math; from math import sqrt; import statistics as st
Main guardif __name__ == "__main__": main()

Common Pitfalls & Developer Mistakes

✕ Mistake 1:

Forgetting to close a file (or not using with)

Why it happens: Written data may not reach the disk and the file stays locked on some systems.

Correction: Always use with open(...) as f:
✕ Mistake 2:

Opening the data file with "w" to update it

Why it happens: The old contents are erased before you read them.

Correction: Read first, change the data in memory, then write the whole file back.
✕ Mistake 3:

Comparing lines without removing the newline

Why it happens: "yes\n" == "yes" is False.

Correction: line.strip() or line.rstrip("\n")
✕ Mistake 4:

Naming your own file random.py, json.py or csv.py

Why it happens: Your file shadows the standard library module and imports break in confusing ways.

Correction: Choose names that do not clash with standard modules.

Frequently Asked Questions

Text mode or binary mode?

Use text mode (the default) with an encoding for anything human-readable. Use "rb" / "wb" for images, archives and other bytes.

Where does open("data.txt") look for the file?

In the current working directory, the folder you started the program from. Build paths relative to the script with Path(__file__).parent / "data.txt" when the file sits next to your code.

What is the difference between a module and a package?

A module is a single .py file. A package is a folder of modules (traditionally with an __init__.py file) that can be imported as a group, such as collections or json.

Hands-On Practice Problems

Article Tags:#Python#Files#CSV#JSON#Modules#Standard Library
Was this lesson clear and helpful?

Discussion (0)

Sign in to ask questions and reply.
Loading discussion…
Discuss in community