Week 1 · Python and your developer toolkit
Live session part 2: files, JSON and errors
Session outline and demo code for the second live session, from reading and writing files to handling errors so your scripts fail clearly.
Session details
- When and where: see the calendar invite for the time and video link
- Bring: your
basics.pyand practice code from part 1, including yourformat_rupees()function - How to follow along: work in your
~/projects/fde-coursefolder, in a file calledfiles_and_errors.py, and run it withpython3 files_and_errors.py. Python creates and looks for files in the folder you run it from, socdinto your course folder first. - Practice: each demo ends with five practice problems that get harder as you go. Problems 1 to 3 are the core: start them in the practice time after the demo and finish them after the session. Problems 4 and 5 are stretch problems for when you finish early or want more practice.
Agenda
| Time | Segment | Format |
|---|---|---|
| 0:00 | Check-in: questions from part 1 and the practice problems | Whole group |
| 0:10 | Demo 1: reading and writing files + practice | Live coding, then practice |
| 0:35 | Demo 2: saving and loading data with JSON + practice | Live coding, then practice |
| 1:00 | Break | |
| 1:10 | Demo 3: handling errors + practice | Live coding, then practice |
| 1:40 | Demo 4: putting it together, an order file checker | Live coding |
| 1:55 | Wrap-up: your independent work this week | Instructor |
Demo 1: reading and writing files
Everything you've written so far forgets its data when the script ends. Files let your data outlive the script. Open a file with open(), giving it a mode: "w" to write:
with open("notes.txt", "w") as file:
file.write("Call Noor Foods about order A102\n")
file.write("Send invoice to Karachi Crafts\n")
\n is a newline: it ends the line. The with block closes the file for you when it finishes, even if something goes wrong. Always use it.
To read the file back, open it without a mode (reading is the default) and call .read():
with open("notes.txt") as file:
text = file.read()
print(text)
Often you want one line at a time. Loop over the file, and use .strip() to remove the newline at the end of each line:
with open("notes.txt") as file:
for line in file:
print(f"- {line.strip()}")
# - Call Noor Foods about order A102
# - Send invoice to Karachi Crafts
| Mode | Meaning | If the file already exists |
|---|---|---|
"r" (default) | Read | Reads it |
"w" | Write | Erases it and starts again |
"a" | Append | Adds to the end |
Everything you read from a file is a string, even if it looks like a number. Convert it with int() or float() before doing arithmetic.
Demo 2: saving and loading data with JSON
Plain text works for simple lists, but real data has structure: dictionaries inside lists inside dictionaries. JSON (JavaScript Object Notation) is a text format for exactly that, and it's how almost every web API sends data. You'll use it constantly from week 3 onwards.
Python's json module turns your data into JSON text and back. import loads a module that comes with Python; you'll learn more about modules in week 2:
import json
order = {"id": "A102", "customer": "Noor Foods", "total": 12800, "paid": False, "notes": None}
text = json.dumps(order)
print(text)
# {"id": "A102", "customer": "Noor Foods", "total": 12800, "paid": false, "notes": null}
same_order = json.loads(text)
print(same_order["customer"]) # Noor Foods
JSON looks a lot like Python, with a few differences:
| Python | JSON |
|---|---|
dict | object: {"key": value} |
list | array: [1, 2, 3] |
"text" or 'text' | "text" (double quotes only) |
True, False | true, false |
None | null |
To save data to a file and load it back, use json.dump() and json.load(), without the s. indent=2 makes the file readable:
orders = [
{"id": "A101", "total": 4500, "status": "delivered"},
{"id": "A102", "total": 12800, "status": "pending"},
{"id": "A103", "total": 2300, "status": "delivered"},
]
with open("orders.json", "w") as file:
json.dump(orders, file, indent=2)
with open("orders.json") as file:
loaded = json.load(file)
print(len(loaded)) # 3
print(loaded[1]["status"]) # pending
Unlike a text file, the numbers come back as numbers: JSON remembers the types for you.
Demo 3: handling errors
When something goes wrong, Python stops and shows a traceback. Read it from the bottom up: the last line names the error and what went wrong, and the lines above show where.
amount = int("12,800")
# ValueError: invalid literal for int() with base 10: '12,800'
Real data is messy, and files go missing. You can't prevent every problem, but you can decide what happens when one occurs. Wrap the risky code in try, and say what to do instead in except:
user_input = "twelve"
try:
quantity = int(user_input)
print(f"Quantity: {quantity}")
except ValueError:
print(f"'{user_input}' is not a whole number")
# 'twelve' is not a whole number
Catch the specific error you expect. Here, a missing file isn't a bug: it just means there are no orders yet:
import json
try:
with open("orders.json") as file:
orders = json.load(file)
except FileNotFoundError:
print("orders.json not found: starting with no orders")
orders = []
Your own functions can raise errors too. When a function gets a value it can't work with, raise an error with a clear message instead of returning a wrong answer:
def apply_discount(total, rate):
if rate < 0 or rate > 1:
raise ValueError(f"Discount rate must be between 0 and 1, got {rate}")
return total - total * rate
apply_discount(12800, 10)
# ValueError: Discount rate must be between 0 and 1, got 10
A few rules that will save you hours:
- Catch specific errors, such as
ValueErrororFileNotFoundError. A bareexcept:catches everything, including your own typos, and hides the bugs you need to see. - Only catch an error if you know what to do about it. Otherwise, let it stop the program with its message.
- Fail clearly. A message that says what went wrong and what value caused it is worth far more than a script that silently prints the wrong total.
Demo 4: putting it together
To finish, we'll combine functions, files, JSON and error handling into one small script that an FDE might actually hand to a customer: it reads an order file, checks it and writes a summary.
import json
def load_orders(path):
try:
with open(path) as file:
return json.load(file)
except FileNotFoundError:
print(f"{path} not found")
return []
except json.JSONDecodeError as error:
print(f"{path} is not valid JSON: {error}")
return []
orders = load_orders("orders.json")
delivered_total = 0
for order in orders:
if order.get("status") == "delivered":
delivered_total += order.get("total", 0)
summary = {"order_count": len(orders), "delivered_total": delivered_total}
with open("summary.json", "w") as file:
json.dump(summary, file, indent=2)
print(f"Wrote summary.json: {summary}")
# Wrote summary.json: {'order_count': 3, 'delivered_total': 6800}
Then we'll break it on purpose: delete orders.json, then add a stray comma, and watch the script report the problem clearly instead of crashing.
After the session
- Finish the practice problems you didn't get to in the session.
- Finish the daily task above.
- Do the self-paced guided build to put your course folder on GitHub, then start the assignment.
- Write down any error you couldn't fix, with the exact message, and bring it to the next session.
Finished this lesson?