Warm-up · Activity 1 of 7
Warm-up from the last lesson: what does this print?
print(f"{'pen':<5}|{3:>3}|")// B5.3 · ~25 min · Beginner
After this lesson you can save text to a file and read it back: pick the right mode, keep the file inside a with block, and handle the newline at the end of every line.
You will be able to
Warm-up · Activity 1 of 7
print(f"{'pen':<5}|{3:>3}|")Predict · Activity 2 of 7
with open("notes.txt", "w", encoding="utf-8") as f:
f.write("one")
f.write("two")
with open("notes.txt", encoding="utf-8") as f:
print(f.read())Practice · Activity 3 of 7
for word in ["red", "green"]:
with open("log.txt", "____", encoding="utf-8") as f:
f.write(word + "\n")Practice · Activity 4 of 7
for word in ["red", "green"]:
with open("log.txt", "w", encoding="utf-8") as f:
f.write(word + "\n")
with open("log.txt", encoding="utf-8") as f:
print(f.read().split())Practice · Activity 5 of 7
Brain teaser · Activity 6 of 7
with open("names.txt", "w", encoding="utf-8") as f:
f.write("Ada\nGrace\n")
with open("names.txt", encoding="utf-8") as f:
lengths = [len(line) for line in f]
print(lengths)Apply · Activity 7 of 7
Check your work against this list
Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.
Worked example
The program writes a shopping list with "w", adds one more item with "a", and reads the file twice: once line by line to number the items, and once with read() to look at the raw text. repr shows the newlines that write put into the file.
main.py
items = ["apples", "bread", "cheese"]
with open("shopping.txt", "w", encoding="utf-8") as f:
for item in items:
f.write(item + "\n")
with open("shopping.txt", "a", encoding="utf-8") as f:
written = f.write("dates\n")
print("appended", written, "characters")
with open("shopping.txt", encoding="utf-8") as f:
for number, line in enumerate(f, start=1):
print(f"{number}. {line.strip()}")
with open("shopping.txt", encoding="utf-8") as f:
text = f.read()
print(repr(text[:13]), len(text.splitlines()), "lines")
Run it with
python main.pyOutput
appended 6 characters
1. apples
2. bread
3. cheese
4. dates
'apples\nbread\n' 4 linesTab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
Exercise 1 of 2
scores.txt (second tab) holds one name,score pair per line. Read it and print two lines: how many scores there are and their average with one decimal place, then the best name and score. For the given file: 3 scores, average 82.3 and best: ada (90). The tests also try other files, so do not copy the numbers.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
with open("scores.txt", encoding="utf-8") as f: then for line in f: gives one line at a time.
line.strip().split(",") turns "ada,90\n" into ["ada", "90"]. Convert the score with int().
Collect the pairs in a dict. max takes a key= just as sorted() does in lesson B3.4, so max(scores, key=lambda name: scores[name]) gives the name with the highest score, and {average:.1f} rounds to one decimal.
One way to solve it. Yours can look different and still pass the checks.
scores: dict[str, int] = {}
with open("scores.txt", encoding="utf-8") as f:
for line in f:
name, score = line.strip().split(",")
scores[name] = int(score)
average = sum(scores.values()) / len(scores)
best = max(scores, key=lambda name: scores[name])
print(f"{len(scores)} scores, average {average:.1f}")
print(f"best: {best} ({scores[best]})")
Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.py
# Read scores.txt: one "name,score" pair per line.
print("3 scores, average 82.3")
print("best: ada (90)")
test_main.py
from learnrun import run_main
def run_with(text):
with open("scores.txt", encoding="utf-8") as f:
original = f.read()
with open("scores.txt", "w", encoding="utf-8") as f:
f.write(text)
try:
return run_main().strip().splitlines()
finally:
with open("scores.txt", "w", encoding="utf-8") as f:
f.write(original)
def test_given_file():
"""The given file gives 3 scores, average 82.3, best ada"""
got = run_main().strip().splitlines()
assert got == ["3 scores, average 82.3", "best: ada (90)"], f"the program printed {got!r}"
def test_other_file():
"""Another file gives its own count, average and best"""
got = run_with("lin,70\nmo,95\n")
assert got == ["2 scores, average 82.5", "best: mo (95)"], f"for lin,70 and mo,95 the program printed {got!r}"
def test_no_final_newline():
"""A file whose last line has no newline still works"""
got = run_with("kim,60\nsam,61")
assert got == ["2 scores, average 60.5", "best: sam (61)"], f"for kim,60 and sam,61 the program printed {got!r}"
scores.txt
ada,90
grace,85
alan,72
On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.
Run the program:
python main.pyRun the checks (needs learnrun.py in the same folder):
python learnrun.py testDownload learnrun.pyExercise 2 of 2
Write save_lines(path, lines), which writes each string of the list to the file on its own line, replacing what was there, and load_lines(path), which returns the lines as a list without their newlines. save_lines("todo.txt", ["milk", "eggs"]) makes the file text exactly "milk\neggs\n". Use with and encoding="utf-8" in both.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
Mode "w" empties the file first, so a second save replaces the first one. "a" would add to it.
write adds no newline: write line + "\n" for each item.
When reading, remove the newline from each line: [line.rstrip("\n") for line in f]. strip() also works, but it would remove spaces at the ends too.
One way to solve it. Yours can look different and still pass the checks.
def save_lines(path: str, lines: list[str]) -> None:
with open(path, "w", encoding="utf-8") as f:
for line in lines:
f.write(line + "\n")
def load_lines(path: str) -> list[str]:
with open(path, encoding="utf-8") as f:
return [line.rstrip("\n") for line in f]
if __name__ == "__main__":
save_lines("todo.txt", ["milk", "eggs"])
print(load_lines("todo.txt"))
Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.py
def save_lines(path: str, lines: list[str]) -> None:
with open(path, "a", encoding="utf-8") as f:
for line in lines:
f.write(line)
def load_lines(path: str) -> list[str]:
with open(path, encoding="utf-8") as f:
return list(f)
if __name__ == "__main__":
save_lines("todo.txt", ["milk", "eggs"])
print(load_lines("todo.txt"))
test_main.py
from main import load_lines, save_lines
def test_file_text():
"""save_lines writes one line per item, each ending in a newline"""
save_lines("t1.txt", ["milk", "eggs"])
with open("t1.txt", encoding="utf-8") as f:
text = f.read()
assert text == "milk\neggs\n", f"the file holds {text!r}, expected 'milk\\neggs\\n'"
def test_round_trip():
"""load_lines gives back exactly what was saved, umlauts included"""
lines = ["Grüße", "tea for two", "42"]
save_lines("t2.txt", lines)
got = load_lines("t2.txt")
assert got == lines, f"load_lines returned {got!r}, expected {lines!r}"
def test_replaces():
"""Saving again replaces the old lines"""
save_lines("t3.txt", ["old"])
save_lines("t3.txt", ["new"])
got = load_lines("t3.txt")
assert got == ["new"], f"after saving ['old'] and then ['new'], load_lines returned {got!r}"
On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.
Run the program:
python main.pyRun the checks (needs learnrun.py in the same folder):
python learnrun.py testDownload learnrun.pywith open("scores.txt", encoding="utf-8") as f:
print(f.read())
What Python prints
No such file or directory: 'scores.txt'Why, and the fix
The full line is FileNotFoundError: [Errno 2] No such file or directory: 'scores.txt' (in the browser the number is 44). Mode "r" never creates a file. Check the spelling, and that the file is in the folder the program runs in; on your computer that is the folder you ran python from.
count = 42
with open("count.txt", "w", encoding="utf-8") as f:
f.write(count)
What Python prints
TypeError: write() argument must be str, not intWhy, and the fix
write only takes strings, unlike print. Convert first, and add the newline yourself: f.write(str(count) + "\n") or f.write(f"{count}\n"). When you read it back, int(line) turns the text into a number again.
with open("notes.txt", "w", encoding="utf-8") as f:
f.write("first line\n")
with open("notes.txt", encoding="utf-8") as f:
f.write("second line\n")
What Python prints
io.UnsupportedOperation: not writableWhy, and the fix
Without a mode, open uses "r", which only reads. To add a line, open with "a"; to replace the content, open with "w".
Python in the browser: Pyodide 314.0.7, MPL-2.0. Licence and source
5 questions, no hints. Score 80% or more to complete the lesson.
Finish every activity above to unlock the exit ticket.