Covers open(), modes, and the with statement. Also see the pathlib docs for modern path handling.
with Statement (Context Manager)This is Python's answer to resource management — like Node.js's fs.open() + close, but automatic. The with block guarantees cleanup even if an exception occurs.
const fs = require('fs');
// Old style — manual close
const fd = fs.openSync('file.txt', 'r');
try {
const content = fs.readFileSync('file.txt', 'utf8');
console.log(content);
} finally {
fs.closeSync(fd);
}
// Modern — streams / promises
const content = await fs.promises.readFile(
'file.txt', 'utf8'
);
# with automatically closes the file
with open("file.txt", "r") as f:
content = f.read()
# file is closed here — guaranteed
# No with (avoid this)
f = open("file.txt", "r")
content = f.read()
f.close() # easy to forget / skip on error
The with statement calls __enter__ on open and __exit__ on close — even if an exception is raised. This is the correct, idiomatic Python pattern. You'll see it everywhere.
| Mode | Meaning | File must exist? |
|---|---|---|
"r" | Read (default) | Yes — FileNotFoundError otherwise |
"w" | Write — truncates first | No — creates it |
"a" | Append | No — creates it |
"x" | Create — fails if exists | No — must NOT exist |
"r+" | Read + write | Yes |
"rb", "wb" | Binary mode | Depends on r/w |
# Read entire file as string
with open("data.txt", "r", encoding="utf-8") as f:
content = f.read()
# Read line by line (memory-efficient for large files)
with open("data.txt", "r") as f:
for line in f:
print(line.strip()) # strip() removes trailing \n
# Read all lines into a list
with open("data.txt", "r") as f:
lines = f.readlines() # [" line1\n", "line2\n", ...]
# Write
with open("output.txt", "w", encoding="utf-8") as f:
f.write("Hello, World!\n")
f.writelines(["line1\n", "line2\n"])
# Append
with open("log.txt", "a") as f:
f.write(f"Event logged\n")
Use encoding="utf-8" explicitly. Without it, Python uses the system default (which can differ between macOS, Windows, Linux). This prevents subtle bugs with non-ASCII characters.
pathlib.Path is the modern, OOP way to work with file paths. Think of it as the path module in Node.js — but object-oriented.
from pathlib import Path
# Create a path object
p = Path("data/users.txt")
base = Path.home() # /Users/rax
cwd = Path.cwd() # current working directory
# Path operations — use / operator to join (not os.path.join!)
config_file = base / ".config" / "app.json"
# Inspect
p.exists() # True/False
p.is_file() # True/False
p.is_dir() # True/False
p.suffix # ".txt"
p.stem # "users"
p.name # "users.txt"
p.parent # Path("data")
# Create directories
Path("output/logs").mkdir(parents=True, exist_ok=True)
# Glob (like glob.glob but better)
for py_file in Path(".").glob("**/*.py"):
print(py_file)
# Read/write directly from Path (Python 3.5+)
text = p.read_text(encoding="utf-8")
p.write_text("new content", encoding="utf-8")
# List directory contents
for item in Path(".").iterdir():
print(item.name)
// Parse
const obj = JSON.parse(jsonString);
// Stringify
const str = JSON.stringify(obj, null, 2);
// File (Node.js)
const data = JSON.parse(
fs.readFileSync('data.json', 'utf8')
);
import json
# Parse string → dict
obj = json.loads(json_string)
# Serialize dict → string
s = json.dumps(obj, indent=2)
# Read from file
with open("data.json") as f:
data = json.load(f) # load (not loads)
# Write to file
with open("output.json", "w") as f:
json.dump(data, f, indent=2) # dump (not dumps)
import csv
# Read CSV
with open("data.csv", newline="") as f:
reader = csv.DictReader(f) # rows as dicts using header row
for row in reader:
print(row["name"], row["age"])
# Write CSV
with open("output.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["name", "age"])
writer.writeheader()
writer.writerow({"name": "Alice", "age": 30})
Any class with __enter__ and __exit__ works as a context manager. Or use the simpler @contextmanager decorator:
from contextlib import contextmanager
import time
@contextmanager
def timer(label):
start = time.perf_counter()
try:
yield # execution pauses here while the with block runs
finally:
elapsed = time.perf_counter() - start
print(f"{label}: {elapsed:.3f}s")
with timer("database query"):
results = db.query("SELECT * FROM users")
# prints: "database query: 0.042s"
1. What guarantees does the with statement provide?
2. What is the difference between json.load() and json.loads()?
3. How do you join two paths with pathlib?
4. Opening a file with mode "w" on an existing file will…
Questions answered correctly.