Python105 min total · 18 parts
Python Fundamentals for Interviews: Data Structures, Comprehensions, and Gotchas
Part 16 of 18 · ~1 min
Type Hints
Nothing about writing ip: str on a parameter makes Python check, at any point, that the caller actually passed a string. Call parse_line(404) and it runs exactly as far as it would have without the hint, right up until something inside tries to call .split() on an integer and fails — the annotation never intervened. What the hint does is give a separate tool something to check on your behalf: mypy or pyright can read ip: str and flag parse_line(404) as wrong before the program ever runs, an editor can use it to autocomplete .split( the moment you type raw., and a reader six months from now gets the function's contract without needing a comment that could quietly go stale.
def parse_line(line: str) -> LogEntry:
...
from typing import Optional
def lookup_geo(ip: str) -> Optional[dict]: # returns a dict, or None if the lookup failed
...
def lookup_geo(ip: str) -> dict | None: # 3.10+ shorthand for the same thing
...
from dataclasses import dataclass
@dataclass
class LogEntry:
ip: str
method: str
path: str
status: int
duration_ms: int
e = LogEntry("203.0.113.42", "GET", "/api/search", 200, 118)
This is the same LogEntry from the very first chapter's namedtuple, wearing modern clothes. namedtuple gives you named fields and stays a tuple underneath — immutable, unpackable, hashable. @dataclass gives you named, typed fields and stays an ordinary mutable object underneath, generating __init__, __repr__, and __eq__ for you automatically — the same three dunders written by hand for Report two chapters ago. Reach for namedtuple when a record should be frozen the moment it's created; reach for @dataclass when fields might legitimately change afterward, or when a type checker should be watching them.