Learn / Python and FastAPI Backend Engineering
Python Language, Types, and Protocols
A consolidated foundations lesson preserving 3 focused PTesting lessons without duplicating an unrelated authored PTLearn topic.
Course: Python and FastAPI Backend Engineering. Level: Intermediate. Topic: Backend craft.
Stage: basic - Foundation - Language and runtime foundations. Connect python language, types, and protocols to the professional workflow for Python & FastAPI.
Outcomes
- Read and write Python expressions and blocks.
- Use functions to name behavior and control scope.
- Choose loops, comprehensions, and generator expressions appropriately.
- Import modules without causing circular dependencies.
- Explain identity, equality, and mutability.
- Model simple domain data with dataclasses.
- Use composition before inheritance.
- Recognize protocol-style interfaces in Python code.
- Annotate functions, containers, and optional values.
- Use TypedDict, Literal, Protocol, and generics when they clarify contracts.
- Understand how FastAPI and Pydantic use annotations at runtime.
- Avoid treating type hints as a replacement for validation.
Concepts
- Syntax Fundamentals
- Blocks, names, and truth
- Function boundaries
- Python + FastAPI foundations
- Guided practice
- Data Model and OOP
- Identity and mutability
- Dataclasses for domain values
- Typing
- Hints describe contracts
- TypedDict for structured dictionaries
Concept flow
Show how language and runtime foundations moves from trigger to implementation outcome in Python & FastAPI.
- Language model
- Runtime behavior
- Engineering decision
- Verification evidence
Session flow
- Model Syntax Fundamentals (concept, 90 min) — Name the decisions behind Syntax Fundamentals before writing code.
- Read and write Python expressions and blocks.
- Explain where Syntax Fundamentals belongs in subscription billing API.
- Build the vertical slice (walkthrough, 162 min) — Implement the smallest useful slice in legacy/python-fastapi/python-language-types-and-protocols.txt.
- Use functions to name behavior and control scope.
- Connect Blocks, names, and truth to a working example.
- Verify and harden (exercise, 108 min) — Starter code: from dataclasses import dataclass
# your decorator here
class UserId:
value: int
- Choose loops, comprehensions, and generator expressions appropriately.
- Record one risk or follow-up before moving on.
- Expressions, Control Flow, and Functions: Blocks, names, and truth (concept, 60 min) — Python uses indentation to form blocks. Names point to objects, and truth testing is based on bool(value). Empty containers, zero, None, and False are falsey; most other values are truthy.
- Retained source example: Filter valid names
names = ['Ada', '', 'Grace', None]
valid = []
for name in names:
if name:
valid.append(name.upper())
print(valid)
Expected output: ['ADA', 'GRACE']
- Expressions, Control Flow, and Functions: Function boundaries (walkthrough, 60 min) — Functions should accept explicit inputs and return explicit outputs. Default arguments are evaluated once when the function is defined, so mutable defaults can leak state across calls.
- Retained source example: Safe default argument
def add_tag(tags: list[str] | None = None) -> list[str]:
result = [] if tags is None else list(tags)
result.append('api')
return result
print(add_tag())
print(add_tag(['python']))
Expected output: ['api']
['python', 'api']
- Expressions, Control Flow, and Functions: practices and mistakes (review, 5 min) — Retained guidance from the legacy PTesting lesson.
- Practice: Prefer small functions with names that explain intent.
- Practice: Use comprehensions for simple transformations, not complex branching.
- Practice: Keep imports at module top unless delaying an import solves a real cycle or startup cost.
- Avoid: Using mutable default arguments.
- Avoid: Hiding complex business rules inside one-line comprehensions.
- Avoid: Catching all exceptions around basic control flow.
- Expressions, Control Flow, and Functions: references (review, 2 min) — Original references retained from the legacy library.
- Python tutorial: https://docs.python.org/3/tutorial/
- Python standard library: https://docs.python.org/3/library/
- Objects, Dataclasses, and Protocols: Identity and mutability (concept, 60 min) — Every Python value is an object. Identity asks whether two names point to the same object; equality asks whether objects compare as equivalent. Mutable objects such as lists and dicts can change in place.
- Retained source example: Identity is not equality
a = [1, 2]
b = [1, 2]
c = a
print(a == b)
print(a is b)
print(a is c)
Expected output: True
False
True
- Objects, Dataclasses, and Protocols: Dataclasses for domain values (walkthrough, 60 min) — Dataclasses remove boilerplate for simple value objects. They are useful inside your application where you want Python objects without tying every concept to a web or database framework.
- Retained source example: A value object
from dataclasses import dataclass
@dataclass(frozen=True)
class Money:
cents: int
currency: str = 'USD'
def display(self) -> str:
return f'{self.currency} {self.cents / 100:.2f}'
print(Money(1299).display())
Expected output: USD 12.99
- Objects, Dataclasses, and Protocols: practices and mistakes (review, 5 min) — Retained guidance from the legacy PTesting lesson.
- Practice: Use dataclasses for internal value objects and Pydantic models for API boundaries.
- Practice: Prefer frozen value objects when data should not change after construction.
- Practice: Design around behavior needed by callers, not around inheritance hierarchy diagrams.
- Avoid: Comparing objects with is when equality was intended.
- Avoid: Mutating lists or dicts shared by multiple parts of the program.
- Avoid: Putting database, HTTP, and domain behavior into one class.
- Objects, Dataclasses, and Protocols: references (review, 2 min) — Original references retained from the legacy library.
- Python tutorial: https://docs.python.org/3/tutorial/
- Python standard library: https://docs.python.org/3/library/
- dataclasses: https://docs.python.org/3/library/dataclasses.html
- typing Protocol: https://docs.python.org/3/library/typing.html#typing.Protocol
- Practical Type Hints for APIs: Hints describe contracts (concept, 60 min) — Type hints document what a function expects and returns. Static type checkers can catch many mismatches before runtime, while frameworks such as FastAPI inspect annotations to validate and document HTTP inputs.
- Retained source example: Typed function boundary
def normalize_email(value: str | None) -> str | None:
if value is None:
return None
return value.strip().lower()
print(normalize_email(' ADA@EXAMPLE.COM '))
Expected output: ada@example.com
- Practical Type Hints for APIs: TypedDict for structured dictionaries (walkthrough, 60 min) — TypedDict is useful when data is naturally dictionary-shaped but still needs documented keys. Pydantic models are usually better at API edges because they validate runtime data.
- Retained source example: Shape a dictionary
from typing import TypedDict
class Health(TypedDict):
status: str
version: str
health: Health = {'status': 'ok', 'version': '1.0'}
print(health['status'])
Expected output: ok
- Practical Type Hints for APIs: practices and mistakes (review, 5 min) — Retained guidance from the legacy PTesting lesson.
- Practice: Annotate public functions and framework boundaries first.
- Practice: Use type aliases for repeated domain concepts.
- Practice: Keep annotations honest; Any should be temporary and justified.
- Avoid: Assuming type hints sanitize untrusted input.
- Avoid: Overusing complex generics where a simple model would be clearer.
- Avoid: Letting annotations drift from real behavior.
- Practical Type Hints for APIs: references (review, 2 min) — Original references retained from the legacy library.
- Python tutorial: https://docs.python.org/3/tutorial/
- Python standard library: https://docs.python.org/3/library/
- typing module: https://docs.python.org/3/library/typing.html
- mypy documentation: https://mypy.readthedocs.io/
Code example
python in legacy/python-fastapi/python-language-types-and-protocols.txt.
def main() -> None:
print("PTLearn foundation")
if __name__ == "__main__":
main()
Walkthrough examples
- Python Language, Types, and Protocols in a subscription billing API — A team is extending a production-style FastAPI service and needs this lesson's pattern to be clear enough for review, testing, and future maintenance.
- File: legacy/python-fastapi/python-language-types-and-protocols.txt
- File: tests/python-language-types-and-protocols.spec
- File: docs/python-fastapi/python-language-types-and-protocols.md
- Start from the provided python snippet and make the intent visible in names and boundaries.
- Apply the checklist item "Explain the language or runtime rule in your own words" before adding extra behavior.
- Write down how the implementation changes when Blocks, names, and truth fails or becomes slow.
- Filter valid names — Additional example retained from the legacy lesson.
- Retained source code:
names = ['Ada', '', 'Grace', None]
valid = []
for name in names:
if name:
valid.append(name.upper())
print(valid)
- Expected output: ['ADA', 'GRACE']
- Compare the example with the canonical PTLearn implementation.
- Safe default argument — Additional example retained from the legacy lesson.
- Retained source code:
def add_tag(tags: list[str] | None = None) -> list[str]:
result = [] if tags is None else list(tags)
result.append('api')
return result
print(add_tag())
print(add_tag(['python']))
- Expected output: ['api']
['python', 'api']
- Compare the example with the canonical PTLearn implementation.
- Comprehension with a named helper — Additional example retained from the legacy lesson.
- Retained source code:
def is_public(path: str) -> bool:
return not path.startswith('/admin')
paths = ['/', '/docs', '/admin/users']
public_paths = [path for path in paths if is_public(path)]
print(public_paths)
- Expected output: ['/', '/docs']
- Compare the example with the canonical PTLearn implementation.
- Identity is not equality — Additional example retained from the legacy lesson.
- Retained source code:
a = [1, 2]
b = [1, 2]
c = a
print(a == b)
print(a is b)
print(a is c)
- Expected output: True
False
True
- Compare the example with the canonical PTLearn implementation.
- A value object — Additional example retained from the legacy lesson.
- Retained source code:
from dataclasses import dataclass
@dataclass(frozen=True)
class Money:
cents: int
currency: str = 'USD'
def display(self) -> str:
return f'{self.currency} {self.cents / 100:.2f}'
print(Money(1299).display())
- Expected output: USD 12.99
- Compare the example with the canonical PTLearn implementation.
- Protocol-shaped dependency — A protocol captures behavior. Any object with send(str) can be used.
- Retained source code:
from typing import Protocol
class Notifier(Protocol):
def send(self, message: str) -> None: ...
def alert_admin(notifier: Notifier) -> None:
notifier.send('service degraded')
- Explain the expected behavior.
- Compare the example with the canonical PTLearn implementation.
- Typed function boundary — Additional example retained from the legacy lesson.
- Retained source code:
def normalize_email(value: str | None) -> str | None:
if value is None:
return None
return value.strip().lower()
print(normalize_email(' ADA@EXAMPLE.COM '))
- Expected output: ada@example.com
- Compare the example with the canonical PTLearn implementation.
- Shape a dictionary — Additional example retained from the legacy lesson.
- Retained source code:
from typing import TypedDict
class Health(TypedDict):
status: str
version: str
health: Health = {'status': 'ok', 'version': '1.0'}
print(health['status'])
- Expected output: ok
- Compare the example with the canonical PTLearn implementation.
- Literal for constrained strings — Additional example retained from the legacy lesson.
- Retained source code:
from typing import Literal
SortOrder = Literal['asc', 'desc']
def order_clause(field: str, order: SortOrder) -> str:
return f'ORDER BY {field} {order.upper()}'
- Explain the expected behavior.
- Compare the example with the canonical PTLearn implementation.
Practice
- Classify HTTP status codes: Choose the expression that returns 'ok' for status codes from 200 through 299 and 'error' otherwise.
- Hint: Use a chained comparison or range membership.
- Reference solution: 'ok' if 200 <= status < 300 else 'error'
- Accepted answers: 'ok' if 200 <= status < 300 else 'error'
- Design a frozen value: Write the decorator line that makes a dataclass immutable after creation.
- Starter code: from dataclasses import dataclass
# your decorator here
class UserId:
value: int
- Hint: The dataclass decorator accepts frozen=True.
- Reference solution: @dataclass(frozen=True)
- Accepted answers: @dataclass(frozen=True)
- Annotate an optional value: Choose the annotation for a value that can be a string or None.
- Starter code: email: ____ = None
- Hint: Modern Python can use the | operator for unions.
- Reference solution: str | None
- Accepted answers: str | None | Optional[str]
Checklist
- Explain the language or runtime rule in your own words
- Run one focused example and record its output
- Name one failure mode or tradeoff
- Keep the verification evidence with the lesson
Quiz prompts
- A teammate wants to hide Syntax Fundamentals inside a convenient helper. What should you check first? — Place Syntax Fundamentals at the boundary that keeps subscription billing API behavior explicit, testable, and reviewable.
- Which artifact best proves this Python & FastAPI lesson is ready for review? — Production-ready learning needs evidence: a test, trace, command, screenshot, or log that catches the risk again.
- What is risky about def f(items=[])? — Default values are evaluated once at function definition time.
- When is composition usually better than inheritance? — Composition lets objects collaborate without creating brittle class trees.
- Do Python type hints automatically validate all values at runtime? — Hints are metadata. Libraries such as Pydantic can use them for runtime validation, but Python itself usually does not enforce them.
Flashcards
- In Python & FastAPI, what should you remember about Syntax Fundamentals? Syntax Fundamentals matters here because it supports "Read and write Python expressions and blocks.".
- In Python & FastAPI, what should you remember about Blocks, names, and truth? Blocks, names, and truth matters here because it supports "Use functions to name behavior and control scope.".
- In Python & FastAPI, what should you remember about Function boundaries? Function boundaries matters here because it supports "Choose loops, comprehensions, and generator expressions appropriately.".
- In Python & FastAPI, what should you remember about Python + FastAPI foundations? Python + FastAPI foundations matters here because it supports "Import modules without causing circular dependencies.".
Labs
- Ship a python language, types, and protocols slice — Extend a production-style FastAPI service with a small but reviewable feature that proves the lesson's architecture in code.
- Classify HTTP status codes: Choose the expression that returns 'ok' for status codes from 200 through 299 and 'error' otherwise.
- Hint: Use a chained comparison or range membership.
- Reference solution: 'ok' if 200 <= status < 300 else 'error'
- Accepted answers: 'ok' if 200 <= status < 300 else 'error'
- Design a frozen value: Write the decorator line that makes a dataclass immutable after creation.
- Starter code: from dataclasses import dataclass
# your decorator here
class UserId:
value: int
- The implementation demonstrates Syntax Fundamentals without hidden global state.
- At least one test or verification step covers the main success path.
- The review notes explain the tradeoff behind Blocks, names, and truth.
Challenge
- Review-ready python language, types, and protocols (Core) — Turn the lesson work into a pull-request-sized change for subscription billing API. Include the code, verification notes, one explicit tradeoff, and an updated concept diagram that names the riskiest handoff.
- All checklist items are either implemented or documented with a reason.
- The change can be understood from legacy/python-fastapi/python-language-types-and-protocols.txt plus one short note.
- The concept diagram names ownership, failure handling, and verification points.
- A teammate could run the verification steps without asking for hidden context.
Canonical lesson URL