File Handling
Everything you've written so far disappears the moment your program ends. Reading and writing files is how a Python program remembers something, or picks up data that lives outside the program itself.
Writing to a file
open() gives you a file object to work with. Pass it a filename and a mode — "w" for write — and use with so the file is automatically closed for you when the block ends:
with open("notes.txt", "w") as f:
f.write("First line\n")
f.write("Second line\n")
print("Done writing.")
Done writing.
Nothing prints from inside the with block itself — this creates notes.txt in the current folder (or overwrites it completely if it already existed, since "w" mode starts from empty) containing the two lines just written.
Reading a file back
"r" mode opens a file for reading. .read() grabs the whole thing as one string, while looping over the file object directly gives you it one line at a time:
with open("notes.txt", "r") as f:
contents = f.read()
print(contents)
First line Second line
with open("notes.txt", "r") as f:
for line in f:
print("Line:", line.strip())
Line: First line Line: Second line
.strip() removes the trailing newline character that comes along with each line — without it, you'd get a blank-looking extra line after every value printed.
Appending instead of overwriting
"a" mode adds to the end of an existing file instead of replacing it:
with open("notes.txt", "a") as f:
f.write("Third line\n")
with open("notes.txt", "r") as f:
print(f.read())
First line Second line Third line
with statement matters more than it looks. It guarantees the file gets closed — flushing anything still buffered to disk and releasing the file handle — even if an error happens partway through the block. Opening a file with plain f = open(...) and forgetting f.close() can leave writes unflushed or, in a long-running program, eventually exhaust the number of files your program is allowed to have open at once.