1. Why Programs Need to Remember Things
Think of a notebook. While you're writing in it, all your notes exist. When you close it and put it down, those notes are still there — because they're on paper, not just in your head. Now imagine if every time you closed your notebook, every page went blank. That would be useless.
That's exactly how a Python program works with its variables. Every number, name, and list you store in a variable lives in RAM — temporary memory. The moment the program stops running, all of it is gone. Files are your program's notebook: they let data survive after the program closes.
Three File Formats You'll Use Most
- .txt files — plain text, one line after another. Simple and universal. Good for logs, notes, and raw data.
- .csv files — "Comma Separated Values." A table in text form — each row is a line, each column is separated by a comma. This is how spreadsheets export data, and how Python reads it.
- .json files — structured data in key-value pairs, like a Python dictionary. Used heavily in APIs, web apps, and config files. Easy for both humans and machines to read.
Variables store data temporarily in RAM. Files store data permanently on disk. Any time your program needs to remember something after it closes — use a file (or a database).
2. Reading Files
Python makes reading files remarkably straightforward. The main function you'll use is open() — it opens a file and gives you a file object you can read from.
The open() Function and Modes
The second argument to open() is the mode — a short string telling Python what you want to do with the file:
- 'r' — Read (default). Open for reading. Fails if the file doesn't exist.
- 'w' — Write. Creates the file if it doesn't exist. Wipes it clean if it does.
- 'a' — Append. Adds to the end of the file without deleting what's there.
Three Ways to Read
- read() — reads the entire file as one big string
- readline() — reads one line at a time (useful for very large files)
- readlines() — reads all lines and returns them as a list
Always Use the with Statement
When you open a file, your program holds on to it (like holding a lunch box open). If your program crashes before you close it, the file can get corrupted or stay locked. The with statement puts a lid on the lunch box for you — automatically closing the file when the block ends, even if something goes wrong.
# Suppose marks.txt contains:
# Arjun,85
# Priya,92
# Rahul,78
with open('marks.txt', 'r') as file:
for line in file:
line = line.strip() # remove the newline at the end
print(line)
# Output:
# Arjun,85
# Priya,92
# Rahul,78
with open('marks.txt', 'r') as file:
lines = file.readlines()
# lines is now: ['Arjun,85\n', 'Priya,92\n', 'Rahul,78\n']
# Process each student:
for entry in lines:
name, mark = entry.strip().split(',')
print(f"{name} scored {mark}")
Create a text file called subjects.txt with five subject names, one per line. Write Python code to read it and print each subject with a number in front — like a numbered list.
3. Writing and Appending Files
Reading is great for getting data in. Writing lets your program save results, logs, and reports that other people (or programs) can use later.
Writing with 'w' Mode
Mode 'w' creates a new file if it doesn't exist, or overwrites the existing one completely if it does. Think of it as tearing out all the old pages and starting fresh.
students = [
('Aisha', 88),
('Dev', 74),
('Meera', 95),
]
with open('results.txt', 'w') as file:
file.write("Student Results\n")
file.write("=" * 30 + "\n")
for name, mark in students:
file.write(f"{name}: {mark}\n")
print("File written successfully.")
Opening a file with 'w' mode deletes everything that was in it before — instantly and silently. Always double-check your mode before writing to an existing file with important data.
Appending with 'a' Mode
Mode 'a' is safer when you want to add new data to an existing file without destroying the old data. It's like writing on the next blank page of a notebook instead of tearing out the existing ones.
# This runs later — adds one more student without wiping the file
with open('results.txt', 'a') as file:
file.write("Kiran: 91\n")
print("New student added.")
4. Error Handling — Building Crash-Proof Programs
A seatbelt doesn't mean you're a bad driver. It means you're a smart one. You wear it not because you expect to crash, but because if something unexpected happens, you want to walk away. Error handling in Python is exactly that seatbelt.
Without error handling, a single unexpected situation — a file that doesn't exist, a user typing letters where a number was expected, a division by zero — will bring your entire program crashing down with a red error message. With error handling, your program can catch the problem, tell the user something helpful, and keep running.
The try / except Block
You put the code that might fail inside the try block. If it fails, Python jumps to the except block instead of crashing.
try:
result = 10 / 0 # This will cause a ZeroDivisionError
print(result)
except ZeroDivisionError:
print("You can't divide by zero!")
Catching Specific Exceptions
Python has many built-in exception types. Being specific about which exception you're catching makes your code clearer and prevents accidentally hiding real bugs:
- FileNotFoundError — the file you tried to open doesn't exist
- ValueError — the value is the wrong type (e.g., trying to convert "hello" to an int)
- ZeroDivisionError — dividing by zero
- IndexError — accessing a list index that doesn't exist
- KeyError — accessing a dictionary key that doesn't exist
else and finally
The else block runs only if no exception occurred — your "success" path. The finally block runs no matter what — useful for cleanup code like closing a connection.
def read_marks_file(filename):
try:
with open(filename, 'r') as file:
content = file.read()
except FileNotFoundError:
print(f"Sorry, '{filename}' was not found.")
print("Please check the file name and try again.")
return None
except PermissionError:
print(f"No permission to read '{filename}'.")
return None
else:
print("File read successfully!")
return content
finally:
print("Attempted to read file.") # Always runs
data = read_marks_file('marks.txt')
if data:
print(data)
Raising Your Own Exceptions
Sometimes you want to deliberately trigger an error when something is wrong with the data your program receives. You can raise exceptions yourself using raise:
def set_marks(mark):
if not isinstance(mark, (int, float)):
raise TypeError("Marks must be a number.")
if mark < 0 or mark > 100:
raise ValueError(f"Marks must be between 0 and 100, got {mark}.")
return mark
try:
m = set_marks(110)
except ValueError as e:
print(f"Invalid input: {e}")
5. Practical Project: Student Results Manager
Let's put it all together. This program reads student names and marks from a CSV file, calculates their grade, and writes a formatted results file. This is a real pattern — it's how data processing scripts are written in the industry.
First, create a file called students.csv with this content:
name,marks Aisha,88 Dev,74 Meera,95 Kiran,62 Arjun,45
def get_grade(mark):
"""Convert a numeric mark to a letter grade."""
if mark >= 90:
return 'A+'
elif mark >= 80:
return 'A'
elif mark >= 70:
return 'B'
elif mark >= 60:
return 'C'
elif mark >= 50:
return 'D'
else:
return 'F'
def process_results(input_file, output_file):
"""Read students.csv, calculate grades, write results.txt."""
students = []
# Read the CSV
try:
with open(input_file, 'r') as f:
lines = f.readlines()
except FileNotFoundError:
print(f"Error: '{input_file}' not found.")
return
# Skip the header row, process the rest
for line in lines[1:]:
line = line.strip()
if not line: # skip empty lines
continue
try:
name, mark_str = line.split(',')
mark = int(mark_str)
grade = get_grade(mark)
students.append((name.strip(), mark, grade))
except ValueError:
print(f"Skipping bad line: '{line}'")
if not students:
print("No valid student data found.")
return
# Write the results file
with open(output_file, 'w') as f:
f.write("STUDENT RESULTS REPORT\n")
f.write("=" * 35 + "\n\n")
for name, mark, grade in students:
f.write(f"{name:<15} {mark:>3}/100 Grade: {grade}\n")
f.write("\n" + "=" * 35 + "\n")
avg = sum(m for _, m, _ in students) / len(students)
f.write(f"Class Average: {avg:.1f}\n")
print(f"Results written to '{output_file}'.")
# Run the program
process_results('students.csv', 'results.txt')
Add a function called search_student(name, filename) that reads the CSV and prints the marks and grade for that specific student. If the student isn't found, print a helpful message instead of crashing.
