try:
value = int(user_input)
except ValueError as e: # catch (NumberFormatException e)
print(f"not a number: {e}")
except (TypeError, KeyError): # multi-catch
...
else: # runs only if NO exception happened (no Java equivalent)
print("parsed", value)
finally: # always runs
cleanup()- No checked exceptions. Nothing forces you to declare or catch. Document raises in docstrings.
raise ValueError("msg")(nonew). Re-raise the current exception with a bareraise.- Never write a bare
except:orexcept Exception: pass, the same as an emptycatch (Exception e) {}.
| Python | Java | When |
|---|---|---|
ValueError |
IllegalArgumentException |
right type, bad value |
TypeError |
ClassCastException-ish |
wrong type |
KeyError |
(map.get returns null instead) |
missing dict key |
IndexError |
IndexOutOfBoundsException |
bad list index |
AttributeError |
NullPointerException (often) |
None.foo |
FileNotFoundError |
FileNotFoundException |
|
NotImplementedError |
UnsupportedOperationException |
|
RuntimeError |
IllegalStateException |
class NotesError(Exception): # base for your library: callers can catch just this
"""Base class for all notes-app errors."""
class NoteNotFoundError(NotesError):
def __init__(self, note_id: int) -> None:
super().__init__(f"note {note_id} not found")
self.note_id = note_id
try:
data = json.loads(text)
except json.JSONDecodeError as e:
raise CorruptStoreError("bad notes file") from e # ≈ new X("...", cause); sets __cause__Python culture prefers EAFP: "Easier to Ask Forgiveness than Permission":
try: # EAFP: Pythonic
note = notes[note_id]
except KeyError:
...
if note_id in notes: # LBYL: "Look Before You Leap", fine too, but racy for files
note = notes[note_id]try (var reader = Files.newBufferedReader(path)) { ... }with open(path, encoding="utf-8") as f: # file is closed even if an exception is raised
content = f.read()Any object with __enter__/__exit__ works with with: files, locks, DB transactions,
HTTP clients. You can write your own:
class Timer:
def __enter__(self):
self.start = time.perf_counter()
return self # bound to the `as` target
def __exit__(self, exc_type, exc, tb): # exception info, or None × 3 on success
self.elapsed = time.perf_counter() - self.start
return False # False/None = don't swallow exceptions
with Timer() as t:
work()
print(t.elapsed)(Lesson 07 shows a shorter way to write these with @contextmanager.)
from pathlib import Path
data_dir = Path.home() / "notes" # `/` joins paths
file = data_dir / "notes.json"
file.parent.mkdir(parents=True, exist_ok=True) # mkdir -p
file.write_text("hello", encoding="utf-8") # one-shot read/write
text = file.read_text(encoding="utf-8")
file.exists(), file.is_file(), file.suffix, file.stem, file.name
for p in data_dir.glob("*.json"): ...
file.unlink(missing_ok=True) # deleteAlways pass encoding="utf-8" for text files.
import json
json.dumps({"a": [1, 2]}, indent=2) # dict → str (≈ Jackson writeValueAsString)
json.loads('{"a": 1}') # str → dict
json.dump(obj, f) / json.load(f) # to/from a file objectJSON maps to dict, list, str, int/float, bool, None. Sets and dataclasses aren't
JSON-serializable: convert them (list(tags), dataclasses.asdict(note)) or use pydantic (Lesson 10).
import csv
with open("out.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["id", "title"])
writer.writeheader()
writer.writerow({"id": 1, "title": "RAG"})
with open("out.csv", newline="", encoding="utf-8") as f:
rows = list(csv.DictReader(f)) # values come back as stringsuv run python lessons/06_errors_files/errors_files_demo.pyexercise.py: save and load notes to JSON safely, with your own exception hierarchy, an atomic
write context manager (no half-written files if the program crashes mid-write), and CSV export.