Python Type Hints Quiz: mypy, PEP 8 and Logging Practice
Test yourself: 12 questions and three programs on Python type hints and narrowing, PEP 8 naming, logging levels and handlers, and Pythonic idioms.
- Course: Python study plan
- Module: Type hints and code quality
- Kind: Checkpoint — cleared at 70%
- Reading time: 22 min
- Runtime: CPython 3.11
Checkpoint — Type hints and code quality is the checkpoint that closes the Type hints and code quality module: a graded quiz and whole-program exercises, passed at 70%.
Instructions
This checkpoint covers the whole module: the hint vocabulary — built-in generics, unions, Callable, TypeVar, Generic, ClassVar, Final, Literal, TypedDict, NewType — with parameters hinted wide and returns narrow; what a type checker verifies and how narrowing, cast, TYPE_CHECKING and get_type_hints work; PEP 8 layout, naming, docstrings and the linter/formatter workflow; logging with levels, the logger hierarchy, formats and handlers; and the idioms and anti-patterns that make code Pythonic.
How it works. Twelve questions and three programs. You need 70% on the questions and every program accepted to clear the module. You can retake it as often as you like; your best score counts.
Before you start, make sure you can answer these from memory:
- Which hint accepts a list, a tuple and a generator of ints, and which requires indexing?
- What does
Optional[int]mean, and what does it not mean? - How does an
is Nonetest change what the checker allows afterwards? - Which naming style applies to functions, classes and constants?
- Why must a library never call
logging.basicConfig? - Why write
log.debug("%s", x)rather than an f-string? - Which five anti-patterns hide bugs rather than just looking foreign?
The three programs are a run-time validator driven by get_type_hints that handles unions and Literals, a linter-lite that flags PEP 8 naming and idiom violations line by line, and a logging pipeline whose records are routed by level to two in-memory handlers with different formats.
Common questions
Which type hint accepts a list, a tuple and a generator, and which requires indexing?
Iterable[int] accepts all three, because each can be looped over; use it when a function only iterates. Sequence[int] requires indexing and slicing, so it accepts lists and tuples but not a generator. Both come from collections.abc.
How does an is None test change what a type checker allows?
It narrows the type. After if x is None: return 0, a value hinted int | None is treated as int for the rest of the function, so calling an int method on it, which was an error before the test, is allowed.
Why must a library never call logging.basicConfig?
Configuring handlers, levels and formats belongs to the application that imports the library, which does it once at start-up. A library that calls basicConfig hijacks the application's output and can make the application's own later call a silent no-op, since basicConfig does nothing once the root logger has handlers.
Exercises
A validator that reads the hints
Given hinted functions greet(name: str, times: int = 1), scale(x: float, by: int | None = None), open_mode(mode: Literal["r", "w"]) and total(xs: list[int]), write check(f, *args) that reads typing.get_type_hints(f) and validates each positional argument: a plain class with isinstance (an int satisfies float; a bool never satisfies int or float); a union (get_origin is types.UnionType) if any member accepts; Literal if the value is one of get_args; list[T] if it is a list whose every element satisfies T. Missing arguments with defaults are fine. Each input line is a name followed by comma-separated argument literals; print ok <result> or type error: <param>.
Input: lines. Output: one line per call.
greet "ada", 2
scale 3, None
open_mode "x"
total [1, 2, "3"]
prints
ok Hello, ada! Hello, ada!
ok 3
type error: mode
type error: xsLinter-lite
Read a Python source and report, per line, every rule it breaks (comma-separated, in rule order): tab, long (over 79 characters), trailing-space, def-name (not snake_case), class-name (not PascalCase), none-compare (== None/!= None), bare-except (except:), mutable-default (=[], ={} or =set() inside a def line's parameters), star-import (import *). Print line <n>: <rules> for each offending line, then problems <total rules>.
Input: source lines. Output: the report and the count.
from os import *
def Load(xs=[]):
try:
pass
except:
pass
prints
line 1: star-import
line 2: def-name, mutable-default
line 5: bare-except
problems 4Routed logging
Build a logger svc at DEBUG with two StringIO handlers: errors at ERROR with format %(levelname)s %(name)s: %(message)s, and all at DEBUG with %(levelname)s: %(message)s. Process lines LEVEL message… through svc, with the special line fail message… raising and catching a RuntimeError and logging it with log.exception (which appends the traceback; count its records but print only their first line). Afterwards print --- errors ---, the first line of each record in the errors buffer, --- all ---, the first line of each record in the all buffer, then records <n> for the all buffer.
Input: lines. Output: as described.
info started
debug detail
fail disk full
error plain error
prints
--- errors ---
ERROR svc: disk full
ERROR svc: plain error
--- all ---
INFO: started
DEBUG: detail
ERROR: disk full
ERROR: plain error
records 4In this module: Type hints and code quality
- Type hints in depth — generics, unions, Callable, TypeVar and Protocol
- Static analysis with mypy — narrowing, strictness and the run-time view of hints
- Code style and PEP 8 — layout, naming, imports, docstrings and the tools that enforce them
- logging — levels, loggers, handlers and formats
- Writing idiomatic Python — the idioms, the anti-patterns and a refactoring
- Checkpoint — Type hints and code quality (this lesson)
← Writing idiomatic Python — the idioms, the anti-patterns and a refactoring · Decorators — functions that wrap functions →