Lesson 8 of 916 min2 challenges

Files and Errors: Making Data Stay

with open, JSON, reading a traceback, and try/except.

Making data outlive the program

Close the program and every variable disappears. Memory is a whiteboard that gets wiped when the lights go out. To keep anything, you write it to a file on disk.

with open("note.txt", "w", encoding="utf-8") as f:
    f.write("first line\n")
    f.write("second line\n")

Nothing prints. A file named note.txt now sits next to your script, containing two lines. Piece by piece:

  • open(filename, mode, encoding) opens the file and hands back a file object.
  • "w" is write mode, and it erases whatever was in the file first. Use "a" to append to the end, "r" to read without changing anything.
  • encoding="utf-8" spells out how characters are stored. Write it every time. Leave it off and your program behaves differently on different machines, which is the worst kind of bug.
  • \n is the newline character. Without it, both writes land on the same line — f.write adds nothing of its own, unlike print.
  • with ... as f closes the file automatically when the block ends, even if an error interrupts it. Always use with. Hand-writing open and close means one early return or one exception leaves the file open and your data possibly unwritten.

Reading it back

with open("note.txt", "r", encoding="utf-8") as f:
    content = f.read()
print(content)

Output:

first line
second line

read() pulls the entire file in as one big string. The blank line at the end is real — the final \n you wrote is still in there, and print adds one more.

For a large file, go line by line instead, so you never hold the whole thing in memory:

with open("note.txt", "r", encoding="utf-8") as f:
    for line in f:
        print(line.strip())

Output:

first line
second line

Looping over a file object gives you one line at a time, each still carrying its trailing \n. strip() removes whitespace from both ends, which is why this version has no extra blank line. Nearly every time you read lines from a file, you want .strip().

Errors are information, not insults

When Python fails, it prints a traceback. It looks intimidating, so people skim it. Don't. Read it from the bottom up. The last line names the error type and the reason. The line above it points at the file and line number. Those two lines carry almost all the information.

The types you'll meet first:

ErrorWhat it meansUsual cause
SyntaxErrorPython couldn't parse the textMissing colon, unbalanced parentheses or quotes
IndentationErrorThe whitespace doesn't line upMixed tabs and spaces
NameErrorUsed a name that doesn't existTypo, or using a variable before assigning it
TypeErrorWrong kind of thingAdding a string to a number
ValueErrorRight kind, impossible valueint("abc")
IndexErrorIndex past the endItem 4 of a 3-item list
KeyErrorNo such key in the dictionaryMisspelled key name
FileNotFoundErrorThe file isn't thereWrong path, or wrong working directory

TypeError versus ValueError is worth a second look, because they sound alike. int("abc") is a ValueError — a string is a perfectly acceptable thing to hand int(), but those particular characters can't become a number. int(["a"]) is a TypeError — a list is not something int() accepts at all.

A note on FileNotFoundError: open("note.txt") looks for the file in the directory your program is running from, not the directory the script lives in. If a file you can see isn't being found, that mismatch is usually why.

Catching errors on purpose

Some failures aren't bugs in your code. They're a person typing something unexpected. Those should be handled, not allowed to crash the program:

try:
    age = int(input("Age: "))
    print(f"Next year you turn {age + 1}")
except ValueError:
    print("Please type a number, not words")

Typing abc gives:

Age: abc
Please type a number, not words

The code inside try runs normally. The moment a ValueError is raised, Python abandons the rest of the try block and jumps to except. The program survives.

The fuller form:

try:
    f = open("data.txt", "r", encoding="utf-8")
except FileNotFoundError:
    print("No such file. I'll create one for you.")
else:
    print("Opened it")
    f.close()
finally:
    print("Done either way")

If data.txt doesn't exist:

No such file. I'll create one for you.
Done either way

else runs only when nothing went wrong. finally runs no matter what — errors, no errors, even an early return. It's for cleanup.

Never write a bare except:. It swallows every error, including your own typos, and turns a clear message into a program that silently does the wrong thing. You'll spend an evening hunting a bug that would have announced itself on line one. Catch the specific type you actually expect.

JSON: saving structures, not text

A text file holds characters. Lists and dictionaries are not characters, so you need a format that can represent them. JSON is that format, and it's the same one used by nearly every web API.

import json

data = {"name": "Maya", "scores": [88, 92]}

with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, indent=2)

with open("data.json", "r", encoding="utf-8") as f:
    loaded = json.load(f)

print(loaded["name"])
print(loaded["scores"][1])

Output:

Maya
92

import json pulls in a module from Python's standard library — code that ships with Python and is ready to use. json.dump writes a dictionary or list to a file. json.load reads one back, rebuilt into real Python dictionaries and lists.

indent=2 formats the file with line breaks and indentation so a human can open it and read it. Leave it off and you get one enormous line. The file on disk looks like this:

{
  "name": "Maya",
  "scores": [
    88,
    92
  ]
}

Two things JSON can't do: it has no idea what a Python set or a datetime is. Convert those to a string or a list before saving. That's why the project in the next lesson stores dates as text.

Ten-second check

You open a file that already has content, using mode "w". Is the old content still there?

No. It's gone, immediately, the moment the file opens — before you write a single byte. To keep what's there and add to the end, use "a".

Everyone loses a file to this once. Reading it here instead is the cheaper option.

Ad slot (AdSense not configured; ads appear here in production)

Hands-on

Reading it isn't knowing it. Writing it is.

0/2 passed

Challenges and grading are completely free, and a wrong answer costs you nothing. Stuck? Hit "Ask the AI": it can see the code you are writing.

Level 01Multiple choice+10 pts · +15 XP

Name That Error

Which error does `int("abc")` raise?

  • Might be multiple choice. Wrong answers cost nothing, so just try.
Level 02Fill in the blank+10 pts · +15 XP

Catch the Error

Complete this code so it survives a non-numeric entry: ```python ____: age = int(input()) ____ ValueError: print("Please enter a number") ```

Finished? Mark it

Marking it done saves your progress, gives you 20 XP and keeps your streak alive.