Files, Errors and Exceptions
Paths with pathlib
from pathlib import Path
data_dir = Path("data")
data_dir.mkdir(exist_ok=True)
report = data_dir / "report.txt"
report.exists()
for p in data_dir.glob("*.csv"):
print(p.name, p.stat().st_size)
pathlib works on every operating system; avoid building paths with string concatenation.
Reading and writing text
report.write_text("Total: 42\n", encoding="utf-8")
content = report.read_text(encoding="utf-8")
with open("log.txt", "a", encoding="utf-8") as fh: # a = append
fh.write("started\n")
with open("big.log", encoding="utf-8") as fh:
for line in fh: # streams line by line, any file size
if "ERROR" in line:
print(line.rstrip())
The with block closes the file even if an error happens. Always pass encoding="utf-8".
CSV and JSON
import csv, json
with open("users.csv", newline="", encoding="utf-8") as fh:
users = list(csv.DictReader(fh))
with open("out.csv", "w", newline="", encoding="utf-8") as fh:
writer = csv.DictWriter(fh, fieldnames=["email", "points"])
writer.writeheader()
writer.writerows(users)
settings = json.loads(Path("settings.json").read_text())
Path("settings.json").write_text(json.dumps(settings, indent=2))
Exceptions
When something goes wrong Python raises an exception. Handle the ones you expect:
try:
quantity = int(input("Quantity: "))
except ValueError:
print("Please enter a whole number.")
else:
print(f"Ordering {quantity}") # runs if no exception
finally:
print("Done") # always runs
- Catch specific exceptions. A bare
except:hides real bugs, including typos. - Do not use exceptions to silence problems: log them or re-raise.
Raising your own
class InsufficientFunds(Exception):
pass
def withdraw(balance, amount):
if amount <= 0:
raise ValueError("amount must be positive")
if amount > balance:
raise InsufficientFunds(f"balance {balance} < {amount}")
return balance - amount
Reading a traceback
Read it from the bottom: the last line is the error type and message; the lines above show the call chain, with your file and line number. Most bugs are found within a minute by reading tracebacks carefully.
Logging instead of print
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger(__name__)
log.info("Imported %d users", len(users))
log.exception("Import failed") # inside except: includes the traceback
Exercise
Write a script that reads orders.csv, skips rows with invalid numbers (logging a warning with the row number), and writes a summary.json with total revenue and order count.
Check yourself
- Why use
with open(...)? - What is wrong with a bare
except:? - Which line of a traceback do you read first?
ההתקדמות שלכם נשמרת בדפדפן הזה בלבד.
שליחה לבדיקה (10 נקודות)