LEARN PYTHON | Type Hints And Documentation

Lesson objective

Write useful annotations and docstrings, distinguish static analysis from runtime validation and document effects and rejection rules accurately.

Learn

06.5 | TYPE HINTS AND DOCUMENTATION

01 | MAKE EXPECTATIONS VISIBLE

A function called find_book receives an identifier and may return a Book. Without a clear signature, a caller must inspect the implementation to discover what to pass and whether absence is possible. Type hints make those expectations visible.

In def double(number: int) -> int, the parameter annotation describes the intended input type and the arrow describes the intended return type. A type checker can analyse source and report likely inconsistencies without executing every route.

Python does not normally enforce these hints at runtime. An annotation is not an automatic converter, a range check or an exception handler. Keep those responsibilities separate.

def double(number: int) -> int:
    return number * 2

print(double(6))

EXPECTED OUTPUT

12

02 | HINT COLLECTION CONTENTS

list[str] describes a list intended to hold strings. dict[str, int] describes string keys with integer values. tuple[str, int] describes a fixed two-item tuple, while tuple[str, ...] describes a variable-length tuple of strings.

These are declarations about expected use, not runtime filters. Appending an integer to a variable annotated list[str] can still happen in ordinary Python; a checker may report the mismatch.

The examples use Python 3.10 or later so built-in generic syntax and union syntax fit the same baseline. Keep the project’s required version in its handover instructions.

names: list[str] = ["Maya", "Noor"]
scores: dict[str, int] = {"Maya": 8}
result: tuple[str, int] = ("Maya", 8)
print(names[0], scores["Maya"], result[1])

EXPECTED OUTPUT

Maya 8 8

03 | DESCRIBE ABSENCE AND NO RESULT

int | None means an integer or None. A search returning an index may need this annotation because zero is a valid index and None means absent. A caller must handle both possibilities.

A method annotated -> None performs its job without supplying a meaningful result; it still returns the actual value None. It may have side effects such as adding a member or printing output, which a type hint alone does not describe.

Use narrowing: after if position is not None, code can treat the value as an integer. Do not use truthiness to distinguish absence from index zero.

def find_first(values: list[int], target: int) -> int | None:
    for index, value in enumerate(values):
        if value == target:
            return index
    return None

position = find_first([8, 5], 8)
if position is not None:
    print("Found at:", position)

EXPECTED OUTPUT

Found at: 0

04 | ANNOTATE METHODS WITHOUT DUPLICATING SELF

A normal instance method still has self as its first parameter. Annotate explicit arguments and the returned result. Type checkers generally infer the instance type for self inside that class.

__init__ is annotated -> None because it initialises the instance rather than returning the new object. A method returning a Loan can name that class as its result.

An attribute annotation documents expected state. An annotation without an assignment does not necessarily create an instance value that a later method can read; initialise required attributes deliberately.

class Counter:
    def __init__(self, start: int = 0) -> None:
        self._value: int = start

    def increment(self) -> int:
        self._value += 1
        return self._value

print(Counter(4).increment())

EXPECTED OUTPUT

5

05 | STATIC REPORTS AND RUNTIME CHECKS

A static type checker examines source against annotations and its configured rules. It can catch a call supplying text where an integer is expected. It cannot independently prove that every user expense is genuine or that a numeric value lies in your required range.

Runtime validation examines actual values when the program executes. The library explicitly checks copy IDs, date objects and policy numbers even though those inputs are annotated.

The deliberate mismatch below still runs because returning value does not itself require a string operation. A checker should flag the call, while runtime Python returns the integer. This example demonstrates the limit; it is not recommended application use.

def echo_label(value: str) -> str:
    return value

# Deliberate mismatch to demonstrate that hints are not runtime enforcement.
result = echo_label(7)
print(result)
print(type(result).__name__)

EXPECTED OUTPUT

7
int

06 | DOCSTRINGS EXPLAIN WHAT TYPES CANNOT

A docstring is the first string statement in a class, function or method body. It becomes documentation accessible through help() and the object’s __doc__. Use it to describe the job and important rules.

A useful contract says what inputs mean, what is returned, what state changes and which failures are deliberately raised. date alone does not tell a caller whether the due day itself counts as overdue.

Avoid repeating only the signature in prose. Explain that return_book removes an active loan, rejects a date before borrowing and returns the removed record. This communicates behaviour that annotations cannot express.

def is_overdue(due_day, check_day) -> bool:
    """Return whether check_day is after due_day; due day itself is not late."""
    return check_day > due_day

print(is_overdue.__doc__)

EXPECTED OUTPUT

Return whether check_day is after due_day; due day itself is not late.

07 | COMMENTS EXPLAIN DECISIONS

Comments should explain a reason, assumption or non-obvious choice. A comment saying add one above count += 1 repeats the code. A comment explaining that bool must not be accepted as a policy integer records a useful design reason.

Keep comments and docstrings accurate after changes. Outdated documentation can be more misleading than none because a reader may trust a rule the code no longer implements.

Document the project entry point, required Python version, commands, data lifetime and how to run tests. A class signature alone cannot provide that complete handover.

08 | A PROTOCOL CAN DESCRIBE BEHAVIOUR

A Protocol from typing can describe an expected interface without requiring every implementation to inherit from one shared custom base. A sensor interface might require read() -> int. This formalises the duck-typing idea from 06.3 for static analysis.

Protocol definitions and runtime checks are different matters. This introduction does not require them in the project. A small clear method contract may be enough while you are learning.

Do not add Any simply to silence every report. Any relaxes type checking, so use it intentionally when information is truly dynamic. Clear interfaces provide more useful feedback than annotations added only for appearance.

09 | SEPARATE TYPE AND VALUE RULES

Select a runtime value for a function documented as accepting an integer quantity from zero through ten. The model reports the type expectation separately from the application range rule. It does not run a static checker.

10 | USE THREE COMPLEMENTARY CHECKS

Type checking analyses declared interfaces. Runtime validation enforces rules on actual values. Tests compare selected behaviours with independent expectations. These tools overlap but none replaces the others.

For the library, annotate Book, Member and Loan records and service methods. Validate external boundaries. Test duplicate IDs, due-date boundaries and rejected state transitions. Document session-only storage.

A well-documented class is one a caller can use correctly without reading every private line, while still knowing the limits of the promises being made.

PUT YOUR SKILLS TO WORK

Use the Section 6 challenges to practise these ideas independently. Work in your Python editor, predict results and keep a short test record.

Section 6 challenges Python course navigation

MATCH THE PYTHON TERM TO ITS MEANING

Terminology

Terminology

Type hint

An annotation describing intended types for tools and readers.

Annotation

Metadata attached to parameters, results or variables.

Return annotation

A declared intended result type after ->.

Static analysis

Examining source without relying on executing its ordinary workflow.

Type checker

A tool comparing code use with declared or inferred type expectations.

Runtime validation

Checking actual values during execution.

Union type

An expectation allowing alternatives such as int | None.

Generic collection hint

A type expression describing a container and its contents.

None result

The result of a function with no meaningful returned value.

Narrowing

Using a check to reduce the possible types considered in a branch.

Docstring

A documentation string attached to a definition.

__doc__

The attribute holding an object’s documentation string.

Contract

The agreed meaning of inputs, results, effects and rejection rules.

Comment

Explanatory source text ignored as an instruction.

Protocol

A typing interface describing expected compatible behaviour.

Any

A typing construct allowing dynamically typed use.

Invariant

A condition intended to hold across valid state changes.

Handover documentation

Instructions explaining setup, use, tests and important limits.

Questions

Questions

CHECK YOUR UNDERSTANDING

Select all correct choices. Each exact set earns one point.

1. What do ordinary Python type hints do?
2. What does int | None express?
3. Which checks distinguish a valid index zero from absence?
4. What does -> None on __init__ describe?
5. What must a useful docstring add beyond types?
6. What does list[str] promise by itself at runtime?
7. Which tools complement each other?
8. What is a Protocol mainly useful for here?

EXPLAIN AND COMPARE

Write your answers before revealing each sample.

1. Explain what annotations provide.

2. Interpret list[str] and tuple[str, int].

3. Explain int | None for search.

4. Compare static analysis and runtime validation.

5. Explain a useful docstring.

6. Suggest a useful comment.

7. Describe a Protocol’s role.

8. Evaluate annotations, validation and tests together.

CODE IT | YOUR INDEPENDENT CHALLENGE

Annotate a search and document its None result, then add runtime validation and tests separately.

Work in your Python editor. Keep a working version, explain one design choice and record at least two tests. Use the Learn tab for worked examples before comparing your result.

Flashcards

Flashcards

Click to flip. Select the ideas you need to revisit.

0 cards selected for revision.

    Selections are kept while this page is open.

    Workbook

    Workbook

    COMING SOON

    The workbook for Type Hints And Documentation is coming soon. Complete the matching task and written questions in your exercise book.