Real programs are never one giant file and never crash silently. This chapter covers three survival skills: splitting code into reusable modules, reading and writing files, and handling errors gracefully.
A module is a single Python file holding related functions. A package is a folder of modules. Importing lets you reuse other people's code (and your own) without rewriting it.
import math # whole module
from math import sqrt, pi # specific names
import numpy as np # alias for short usage
Use import module when you want several names or clarity about the source, from module import name when you use one thing constantly, and aliases for long names you type often.
Every module gets a built-in variable called __name__. When you run a file directly, it is "__main__"; when another file imports it, it is the module's own name. This lets one file act as both a library and a runnable script:
def double(x):
return x * 2
if __name__ == "__main__":
print(double(21)) # only runs when this file is executed directly
if __name__ == "__main__" prevents side effects when other files import it.
The standard library ships with Python. These are the modules you will reach for constantly:
os — talk to the operating system: list folders, rename files, read environment variables.sys — access interpreter details: command-line arguments (sys.argv) and the Python path.math — mathematical constants and functions such as sqrt, ceil, and log.datetime — create, format, and compare dates and times.json — convert between Python dictionaries and JSON text, the common data format for APIs.collections — powerful containers such as Counter, defaultdict, and deque.random — pseudo-random numbers, shuffling, and picking items.import os, sys, math, random
from collections import Counter
print(math.sqrt(144)) # 12.0
print(random.choice(["a", "b", "c"])) # random pick
print(Counter("aabbbc")) # Counter({'a': 2, 'b': 3, 'c': 1})
Build your own tiny package. Create a folder shapes/ containing area.py with two functions, then import them elsewhere:
# shapes/area.py
def circle(r):
return 3.14159 * r * r
def square(side):
return side * side
# main.py - run from the folder above shapes/
from shapes.area import circle, square
print(circle(2)) # 12.56636
print(square(3)) # 9
Reading and writing text files is the bridge between your program and data on disk.
f = open("notes.txt", "w") # write mode
f.write("first line\n")
f.close()
Common modes: "r" read, "w" write (overwrites), "a" append, "r+" read and write.
Forgetting close() can corrupt data. The with block closes the file automatically when the block ends:
with open("notes.txt", "a") as f:
f.write("more text\n")
with open("notes.txt") as f:
for line in f:
print(line.strip())
with open("data.txt", "w") as f:
f.write("1,2,3\n4,5,6\n")
with open("data.txt") as f:
content = f.read() # whole file as one string
print(content)
for line in f reads one line at a time without loading the entire file into memory — essential when files are large.
os.path builds paths correctly on any operating system instead of relying on hard-coded slashes:
import os
path = os.path.join("data", "raw", "set.csv")
print(path) # data/raw/set.csv (or with backslashes on Windows)
print(os.path.exists(path)) # False - no such file yet
The modern pathlib module treats paths as objects with useful methods:
from pathlib import Path
p = Path("data") / "raw" / "set.csv"
print(p.name) # set.csv
print(p.parent) # data\raw
print(p.suffix) # .csv
datetime and the append mode.Errors are inevitable — files vanish, users type numbers as text, networks drop. Exception handling lets a program detect a problem and react instead of stopping abruptly.
try:
number = int(input("Enter a number: "))
print(100 / number)
except ValueError:
print("That was not a valid number.")
except ZeroDivisionError:
print("Cannot divide by zero.")
else:
print("The calculation succeeded.")
finally:
print("Cleanup runs no matter what.")
try — the risky code goes here.except — runs only if a matching error occurs.else — runs only if no error occurred.finally — always runs, perfect for closing files or releasing resources.ValueError or ZeroDivisionError rather than a bare except:. A blanket handler hides bugs and makes debugging painful.
"missing" when FileNotFoundError is raised, instead of crashing.try/except so a malformed json.JSONDecodeError produces a friendly message.finally to ensure a file handle is closed even when a write inside the try block fails.journal.txt using with and datetime, then read the whole file back and display it. Wrap the input handling in try/except so an empty entry is politely rejected instead of crashing the program.