Writing a file
Variables live in memory and vanish when a program ends. A file is data saved on disk under a name, such as shop.txt, so it is still there next time. This program writes a shopping list, then reads it back.
open("shop.txt", "w") opens the file for writing: "w" creates it, or empties it if it exists. open() returns a file object, and as f names it. The indented lines below run while the file is open.
f.write("milk\n") hands text to the file object and returns 5, the number of characters. \n is the newline character: it ends a line. For speed, Python first collects the text in a buffer in memory.
write() adds nothing of its own, no space and no newline, so each string ends in \n. The buffer now holds both lines, but shop.txt on disk is still empty: 0 bytes.
Line 4 is not indented, so the with block ends and Python closes f. Closing flushes the buffer: all 10 characters reach shop.txt. Without with, call f.close() yourself, or text can be lost.
Reading it back
open("shop.txt") has no mode, so it uses the default, "r": read text. It returns a new file object, again named f, that starts at the beginning of the file.
for line in f: reads the file one line per turn of the loop. The first turn sets line to 'milk\n'. Each line keeps the newline that ended it in the file.
line.strip() returns a copy without the spaces and newlines at its ends, so print() shows milk. Without strip(), the \n plus print's own newline would leave a blank line after each item.
The second turn sets line to 'eggs\n', printed as eggs. No lines are left, so the loop ends, the with block closes f and the program finishes. shop.txt stays on disk.
When it fails
Change line 4 to open "todo.txt", a file that does not exist. Lines 1 to 3 still run, then Python stops with FileNotFoundError. Reading needs the file to be there; "w" makes one.
Try it yourself
;; step 1/10 # a variable is gone when the program ends: items = ["milk", "eggs"] # a file stays on disk; this program makes one: $ python files.py milk eggs $ ls shop.txt shop.txt
File objects are buffered: small writes wait in memory until the buffer fills, you call f.flush(), or the file is closed. The Python tutorial warns that without with or f.close(), what you wrote might not all reach the disk, even if the program ends normally. The figure shortens the error. The real message shows the full path to files.py, then line 4 itself with ^ marks under the call, and prints the last line in one piece; a narrow terminal wraps it.