Python's dynamic typing is flexible but can lead to runtime errors and unclear APIs. The typing module enables static type checking, better IDE support, and self-documenting code without runtime performance impact, catching bugs before they reach production.

Why Use Type Hints?

  • Better IDE support (autocomplete, refactoring)
  • Catch bugs early with static type checkers
  • Self-documenting code
  • No runtime performance impact

Basic Type Hints

example
basic_hints.py
Replay: real traced execution (multi-file project)
# Basic type hints for functions and variables

def add(a: int, b: int) -> int:
    return a + b

def greet(name: str) -> str:
    return f"Hello, {name}!"

def is_adult(age: int) -> bool:
    return age >= 18

# Variable type hints
username: str = "Alice"
age: int = 30
height: float = 5.8
is_active: bool = True

# Using the functions
result = add(10, 20)
print(f"10 + 20 = {result}")

message = greet("Bob")
print(message)

print(f"Is 25 adult? {is_adult(25)}")
print(f"Is 15 adult? {is_adult(15)}")
# Basic type hints for functions and variables

def add(a: int, b: int) -> int:
    return a + b

def greet(name: str) -> str:
    return f"Hello, {name}!"

def is_adult(age: int) -> bool:
    return age >= 18

# Variable type hints
username: str = "Maya"
age: int = 30
height: float = 5.8
is_active: bool = True

# Using the functions
result = add(10, 20)
print(f"10 + 20 = {result}")

message = greet("Bob")
print(message)

print(f"Is 25 adult? {is_adult(25)}")
print(f"Is 15 adult? {is_adult(15)}")
# Basic type hints for functions and variables

def add(a: int, b: int) -> int:
    return a + b

def greet(name: str) -> str:
    return f"Hello, {name}!"

def is_adult(age: int) -> bool:
    return age >= 18

# Variable type hints
username: str = "Jordan"
age: int = 30
height: float = 5.8
is_active: bool = True

# Using the functions
result = add(10, 20)
print(f"10 + 20 = {result}")

message = greet("Bob")
print(message)

print(f"Is 25 adult? {is_adult(25)}")
print(f"Is 15 adult? {is_adult(15)}")
# Basic type hints for functions and variables

def add(a: int, b: int) -> int:
    return a + b

def greet(name: str) -> str:
    return f"Hello, {name}!"

def is_adult(age: int) -> bool:
    return age >= 18

# Variable type hints
username: str = "Alice"
age: int = 17
height: float = 5.8
is_active: bool = True

# Using the functions
result = add(10, 20)
print(f"10 + 20 = {result}")

message = greet("Bob")
print(message)

print(f"Is 25 adult? {is_adult(25)}")
print(f"Is 15 adult? {is_adult(15)}")
# Basic type hints for functions and variables

def add(a: int, b: int) -> int:
    return a + b

def greet(name: str) -> str:
    return f"Hello, {name}!"

def is_adult(age: int) -> bool:
    return age >= 18

# Variable type hints
username: str = "Alice"
age: int = 45
height: float = 5.8
is_active: bool = True

# Using the functions
result = add(10, 20)
print(f"10 + 20 = {result}")

message = greet("Bob")
print(message)

print(f"Is 25 adult? {is_adult(25)}")
print(f"Is 15 adult? {is_adult(15)}")
  1. username ← Alice, age ← 30, height ← 5.8, is_active ← True

    12# Variable type hints13username→ Alice: str = "Alice"14#@username="Maya", "Jordan"15age→ 30: int = 3016#@age=17, 4517height→ 5.8: float = 5.818is_active→ True: bool = TrueTrue1920# Using the functions21result = add(10, 20)22print(f"10 + 20 = {result}")
  2. def add(a: int, b: int) -> int:

    3def add(a10: int, b20: int) -> int:4    return a10 + b20
  3. result ← 30

    20# Using the functions21result→ 30 = add(10, 20)22print(f"10 + 20 = {result30}")2324message = greet("Bob")25print(message)
    output10 + 20 = 30
  4. def greet(name: str) -> str:

    6def greet(nameBob: str) -> str:7    return f"Hello, {nameBob}!"
  5. message ← Hello, Bob!

    24message→ Hello, Bob! = greet("Bob")25print(messageHello, Bob!)2627print(f"Is 25 adult? {is_adult(25)}")28print(f"Is 15 adult? {is_adult(15)}")
    outputHello, Bob!
  6. def is_adult(age: int) -> bool:

    pass 1 of 2
    9def is_adult(age25: int) -> bool:10    return age25 >= 18
  7. print(f"Is 25 adult? {is_adult(25)}")

    27print(f"Is 25 adult? {is_adult(25)}")28print(f"Is 15 adult? {is_adult(15)}")
    outputIs 25 adult? True
  8. def is_adult(age: int) -> bool:

    pass 2 of 2
    9def is_adult(age15: int) -> bool:10    return age15 >= 18
  9. print(f"Is 15 adult? {is_adult(15)}")

    27print(f"Is 25 adult? {is_adult(25)}")28print(f"Is 15 adult? {is_adult(15)}")
    outputIs 15 adult? False
  1. username ← Maya, age ← 30, height ← 5.8, is_active ← True

    12# Variable type hints13username→ Maya: str = "Maya"14age→ 30: int = 3015height→ 5.8: float = 5.816is_active→ True: bool = TrueTrue1718# Using the functions19result = add(10, 20)20print(f"10 + 20 = {result}")
  2. def add(a: int, b: int) -> int:

    3def add(a10: int, b20: int) -> int:4    return a10 + b20
  3. result ← 30

    18# Using the functions19result→ 30 = add(10, 20)20print(f"10 + 20 = {result30}")2122message = greet("Bob")23print(message)
    output10 + 20 = 30
  4. def greet(name: str) -> str:

    6def greet(nameBob: str) -> str:7    return f"Hello, {nameBob}!"
  5. message ← Hello, Bob!

    22message→ Hello, Bob! = greet("Bob")23print(messageHello, Bob!)2425print(f"Is 25 adult? {is_adult(25)}")26print(f"Is 15 adult? {is_adult(15)}")
    outputHello, Bob!
  6. def is_adult(age: int) -> bool:

    pass 1 of 2
    9def is_adult(age25: int) -> bool:10    return age25 >= 18
  7. print(f"Is 25 adult? {is_adult(25)}")

    25print(f"Is 25 adult? {is_adult(25)}")26print(f"Is 15 adult? {is_adult(15)}")
    outputIs 25 adult? True
  8. def is_adult(age: int) -> bool:

    pass 2 of 2
    9def is_adult(age15: int) -> bool:10    return age15 >= 18
  9. print(f"Is 15 adult? {is_adult(15)}")

    25print(f"Is 25 adult? {is_adult(25)}")26print(f"Is 15 adult? {is_adult(15)}")
    outputIs 15 adult? False
  1. username ← Jordan, age ← 30, height ← 5.8, is_active ← True

    12# Variable type hints13username→ Jordan: str = "Jordan"14age→ 30: int = 3015height→ 5.8: float = 5.816is_active→ True: bool = TrueTrue1718# Using the functions19result = add(10, 20)20print(f"10 + 20 = {result}")
  2. def add(a: int, b: int) -> int:

    3def add(a10: int, b20: int) -> int:4    return a10 + b20
  3. result ← 30

    18# Using the functions19result→ 30 = add(10, 20)20print(f"10 + 20 = {result30}")2122message = greet("Bob")23print(message)
    output10 + 20 = 30
  4. def greet(name: str) -> str:

    6def greet(nameBob: str) -> str:7    return f"Hello, {nameBob}!"
  5. message ← Hello, Bob!

    22message→ Hello, Bob! = greet("Bob")23print(messageHello, Bob!)2425print(f"Is 25 adult? {is_adult(25)}")26print(f"Is 15 adult? {is_adult(15)}")
    outputHello, Bob!
  6. def is_adult(age: int) -> bool:

    pass 1 of 2
    9def is_adult(age25: int) -> bool:10    return age25 >= 18
  7. print(f"Is 25 adult? {is_adult(25)}")

    25print(f"Is 25 adult? {is_adult(25)}")26print(f"Is 15 adult? {is_adult(15)}")
    outputIs 25 adult? True
  8. def is_adult(age: int) -> bool:

    pass 2 of 2
    9def is_adult(age15: int) -> bool:10    return age15 >= 18
  9. print(f"Is 15 adult? {is_adult(15)}")

    25print(f"Is 25 adult? {is_adult(25)}")26print(f"Is 15 adult? {is_adult(15)}")
    outputIs 15 adult? False
  1. username ← Alice, age ← 17, height ← 5.8, is_active ← True

    12# Variable type hints13username→ Alice: str = "Alice"14age→ 17: int = 1715height→ 5.8: float = 5.816is_active→ True: bool = TrueTrue1718# Using the functions19result = add(10, 20)20print(f"10 + 20 = {result}")
  2. def add(a: int, b: int) -> int:

    3def add(a10: int, b20: int) -> int:4    return a10 + b20
  3. result ← 30

    18# Using the functions19result→ 30 = add(10, 20)20print(f"10 + 20 = {result30}")2122message = greet("Bob")23print(message)
    output10 + 20 = 30
  4. def greet(name: str) -> str:

    6def greet(nameBob: str) -> str:7    return f"Hello, {nameBob}!"
  5. message ← Hello, Bob!

    22message→ Hello, Bob! = greet("Bob")23print(messageHello, Bob!)2425print(f"Is 25 adult? {is_adult(25)}")26print(f"Is 15 adult? {is_adult(15)}")
    outputHello, Bob!
  6. def is_adult(age: int) -> bool:

    pass 1 of 2
    9def is_adult(age25: int) -> bool:10    return age25 >= 18
  7. print(f"Is 25 adult? {is_adult(25)}")

    25print(f"Is 25 adult? {is_adult(25)}")26print(f"Is 15 adult? {is_adult(15)}")
    outputIs 25 adult? True
  8. def is_adult(age: int) -> bool:

    pass 2 of 2
    9def is_adult(age15: int) -> bool:10    return age15 >= 18
  9. print(f"Is 15 adult? {is_adult(15)}")

    25print(f"Is 25 adult? {is_adult(25)}")26print(f"Is 15 adult? {is_adult(15)}")
    outputIs 15 adult? False
  1. username ← Alice, age ← 45, height ← 5.8, is_active ← True

    12# Variable type hints13username→ Alice: str = "Alice"14age→ 45: int = 4515height→ 5.8: float = 5.816is_active→ True: bool = TrueTrue1718# Using the functions19result = add(10, 20)20print(f"10 + 20 = {result}")
  2. def add(a: int, b: int) -> int:

    3def add(a10: int, b20: int) -> int:4    return a10 + b20
  3. result ← 30

    18# Using the functions19result→ 30 = add(10, 20)20print(f"10 + 20 = {result30}")2122message = greet("Bob")23print(message)
    output10 + 20 = 30
  4. def greet(name: str) -> str:

    6def greet(nameBob: str) -> str:7    return f"Hello, {nameBob}!"
  5. message ← Hello, Bob!

    22message→ Hello, Bob! = greet("Bob")23print(messageHello, Bob!)2425print(f"Is 25 adult? {is_adult(25)}")26print(f"Is 15 adult? {is_adult(15)}")
    outputHello, Bob!
  6. def is_adult(age: int) -> bool:

    pass 1 of 2
    9def is_adult(age25: int) -> bool:10    return age25 >= 18
  7. print(f"Is 25 adult? {is_adult(25)}")

    25print(f"Is 25 adult? {is_adult(25)}")26print(f"Is 15 adult? {is_adult(15)}")
    outputIs 25 adult? True
  8. def is_adult(age: int) -> bool:

    pass 2 of 2
    9def is_adult(age15: int) -> bool:10    return age15 >= 18
  9. print(f"Is 15 adult? {is_adult(15)}")

    25print(f"Is 25 adult? {is_adult(25)}")26print(f"Is 15 adult? {is_adult(15)}")
    outputIs 15 adult? False
type hint An annotation that specifies the expected type of a variable, function parameter, or return value, enabling static analysis and better documentation.

Collection Types

Use List, Dict, Set, and Tuple for typed collections.

collections.py
Replay: real traced execution (multi-file project)
# Collection type hints

from typing import List, Dict, Set, Tuple

def process_numbers(numbers: List[int]) -> int:
    return sum(numbers)

def get_scores() -> Dict[str, int]:
    return {"Alice": 95, "Bob": 87, "Charlie": 92}

def unique_values(values: List[int]) -> Set[int]:
    return set(values)

def get_coordinates() -> Tuple[float, float, float]:
    return (10.5, 20.3, 30.7)

# Usage
nums = [1, 2, 3, 4, 5]
total = process_numbers(nums)
print(f"Sum: {total}")

scores = get_scores()
print(f"Scores: {scores}")

unique = unique_values([1, 2, 2, 3, 3, 3])
print(f"Unique: {unique}")

x, y, z = get_coordinates()
print(f"Coordinates: ({x}, {y}, {z})")
  1. nums ← [1, 2, 3, 4, 5]

    17# Usage18nums→ [1, 2, 3, 4, 5] = [1, 2, 3, 4, 5]19total = process_numbers(nums[1, 2, 3, 4, 5])20print(f"Sum: {total}")
  2. def process_numbers(numbers: List[int]) -> int:

    5def process_numbers(numbers[1, 2, 3, 4, 5]: List[int]) -> int:6    return sum(numbers[1, 2, 3, 4, 5])
  3. total ← 15

    18nums = [1, 2, 3, 4, 5]19total→ 15 = process_numbers(nums[1, 2, 3, 4, 5])20print(f"Sum: {total15}")2122scores = get_scores()23print(f"Scores: {scores}")
    outputSum: 15
  4. scores ← {'Alice': 95, 'Bob': 87, 'Charlie': 92}

    22scores→ {'Alice': 95, 'Bob': 87, 'Charlie': 92} = get_scores()23print(f"Scores: {scores{'Alice': 95, 'Bob': 87, 'Charlie': 92}}")2425unique = unique_values([1, 2, 2, 3, 3, 3])26print(f"Unique: {unique}")
    outputScores: {'Alice': 95, 'Bob': 87, 'Charlie': 92}
  5. def unique_values(values: List[int]) -> Set[int]:

    11def unique_values(values[1, 2, 2, 3, 3, 3]: List[int]) -> Set[int]:12    return set(values[1, 2, 2, 3, 3, 3])
  6. unique ← {1, 2, 3}

    25unique→ {1, 2, 3} = unique_values([1, 2, 2, 3, 3, 3])26print(f"Unique: {unique{1, 2, 3}}")2728x, y, z = get_coordinates()29print(f"Coordinates: ({x}, {y}, {z})")
    outputUnique: {1, 2, 3}
  7. x ← 10.5, y ← 20.3, z ← 30.7

    28x→ 10.5, y→ 20.3, z→ 30.7 = get_coordinates()29print(f"Coordinates: ({x10.5}, {y20.3}, {z30.7})")
    outputCoordinates: (10.5, 20.3, 30.7)

Optional Type

Use Optional[T] for values that can be None.

optional.py
Replay: real traced execution (multi-file project)
# Optional type for nullable values

from typing import Optional

def find_user(user_id: int) -> Optional[str]:
    """Returns username or None if not found"""
    users = {1: "Alice", 2: "Bob", 3: "Charlie"}
    return users.get(user_id)

def divide(a: float, b: float) -> Optional[float]:
    """Returns result or None if division by zero"""
    if b == 0:
        return None
    return a / b

# Usage
user = find_user(2)
if user is not None:
    print(f"Found user: {user}")
else:
    print("User not found")

user = find_user(999)
if user is not None:
    print(f"Found user: {user}")
else:
    print("User not found")

# Division
result = divide(10, 2)
print(f"10 / 2 = {result}")

result = divide(10, 0)
print(f"10 / 0 = {result}")
  1. user = find_user(2)

    16# Usage17user = find_user(2)18if user is not None:
  2. users ← {1: 'Alice', 2: 'Bob', 3: 'Charlie'}

    pass 1 of 2
    5def find_user(user_id2: int) -> Optional[str]:6    """Returns username or None if not found"""7    users→ {1: 'Alice', 2: 'Bob', 3: 'Charlie'} = {1: "Alice", 2: "Bob", 3: "Charlie"}8    return users{1: 'Alice', 2: 'Bob', 3: 'Charlie'}.get(user_id2)
  3. user ← Bob

    16# Usage17user→ Bob = find_user(2)18if user is not None:
  4. if user is not None:

    17user = find_user(2)18if userBob is not None:19    print(f"Found user: {userBob}")20else:
    outputFound user: Bob
  5. user = find_user(999)

    23user = find_user(999)24if user is not None:
  6. users ← {1: 'Alice', 2: 'Bob', 3: 'Charlie'}

    pass 2 of 2
    5def find_user(user_id999: int) -> Optional[str]:6    """Returns username or None if not found"""7    users→ {1: 'Alice', 2: 'Bob', 3: 'Charlie'} = {1: "Alice", 2: "Bob", 3: "Charlie"}8    return users{1: 'Alice', 2: 'Bob', 3: 'Charlie'}.get(user_id999)
  7. user ← None

    23user→ None = find_user(999)24if user is not None:
  8. else:

    24if user is not None:25    print(f"Found user: {user}")26else:27    print("User not found")
    outputUser not found
  9. result = divide(10, 2)

    29# Division30result = divide(10, 2)31print(f"10 / 2 = {result}")
  10. def divide(a: float, b: float) -> Optional[float]:

    pass 1 of 2
    10def divide(a10: float, b2: float) -> Optional[float]:11    """Returns result or None if division by zero"""12    if b == 0:13        return None14    return a10 / b2
  11. result ← 5.0

    29# Division30result→ 5.0 = divide(10, 2)31print(f"10 / 2 = {result5.0}")3233result = divide(10, 0)34print(f"10 / 0 = {result}")
    output10 / 2 = 5.0
  12. def divide(a: float, b: float) -> Optional[float]:

    pass 2 of 2
    10def divide(a10: float, b0: float) -> Optional[float]:11    """Returns result or None if division by zero"""12    if b == 0:
  13. if b == 0:

    11"""Returns result or None if division by zero"""12if b0 == 0:13    return None14return a / b
  14. result ← None

    33result→ None = divide(10, 0)34print(f"10 / 0 = {resultNone}")
    output10 / 0 = None
Optional A type hint indicating a value can be either the specified type or None, equivalent to Union[T, None].

Union Types

Use Union when multiple types are acceptable.

union.py
Replay: real traced execution (multi-file project)
# Union types for multiple allowed types

from typing import Union

def format_value(value: Union[int, float, str]) -> str:
    """Format different types of values"""
    if isinstance(value, (int, float)):
        return f"Number: {value}"
    return f"Text: {value}"

def process_id(id_value: Union[int, str]) -> str:
    """Process ID that can be int or string"""
    return f"ID-{id_value}"

# Usage
print(format_value(42))
print(format_value(3.14))
print(format_value("hello"))

print(process_id(1001))
print(process_id("ABC123"))

# Function that returns different types
def get_config(key: str) -> Union[str, int, bool]:
    config = {
        "host": "localhost",
        "port": 8080,
        "debug": True
    }
    return config.get(key, "")

print(f"Host: {get_config('host')}")
print(f"Port: {get_config('port')}")
print(f"Debug: {get_config('debug')}")
  1. print(format_value(42))

    15# Usage16print(format_value(42))17print(format_value(3.14))
  2. def format_value(value: Union[int, float, str]) -> str:

    pass 1 of 3
    5def format_value(value42: Union[int, float, str]) -> str:6    """Format different types of values"""7    if isinstance(value, (int, float)):
    All 3 passes — pass 1 is the card above
    passvalue
    142
    23.14
    3hello
  3. if isinstance(value, (int, float)):

    pass 1 of 2
    6"""Format different types of values"""7if isinstance(value42, (int, float)):8    return f"Number: {value42}"9return f"Text: {value}"
  4. print(format_value(42))

    15# Usage16print(format_value(42))17print(format_value(3.14))18print(format_value("hello"))
    outputNumber: 42
  5. if isinstance(value, (int, float)):

    pass 2 of 2
    6"""Format different types of values"""7if isinstance(value3.14, (int, float)):8    return f"Number: {value3.14}"9return f"Text: {value}"
  6. print(format_value(3.14))

    16print(format_value(42))17print(format_value(3.14))18print(format_value("hello"))
    outputNumber: 3.14
  7. print(format_value("hello"))

    17print(format_value(3.14))18print(format_value("hello"))1920print(process_id(1001))21print(process_id("ABC123"))
    outputText: hello
  8. def process_id(id_value: Union[int, str]) -> str:

    pass 1 of 2
    11def process_id(id_value1001: Union[int, str]) -> str:12    """Process ID that can be int or string"""13    return f"ID-{id_value1001}"
  9. print(process_id(1001))

    20print(process_id(1001))21print(process_id("ABC123"))
    outputID-1001
  10. def process_id(id_value: Union[int, str]) -> str:

    pass 2 of 2
    11def process_id(id_valueABC123: Union[int, str]) -> str:12    """Process ID that can be int or string"""13    return f"ID-{id_valueABC123}"
  11. print(process_id("ABC123"))

    20print(process_id(1001))21print(process_id("ABC123"))2223# Function that returns different types24def get_config(key: str) -> Union[str, int, bool]:25    config = {26        "host": "localhost",27        "port": 8080,28        "debug": True29    }30    return config.get(key, "")3132print(f"Host: {get_config('host')}")33print(f"Port: {get_config('port')}")
    outputID-ABC123
  12. config ← {'host': 'localhost', 'port': 8080, 'debug': True}

    pass 1 of 3
    23# Function that returns different types24def get_config(keyhost: str) -> Union[str, int, bool]:25    config→ {'host': 'localhost', 'port': 8080, 'debug': True} = {26        "host": "localhost",27        "port": 8080,28        "debug": True29    }30    return config{'host': 'localhost', 'port': 8080, 'debug': True}.get(keyhost, "")
    All 3 passes — pass 1 is the card above
    passkeyconfig
    1host{'host': 'localhost', 'port': 8080, 'debug': True}
    2port{'host': 'localhost', 'port': 8080, 'debug': True}
    3debug{'host': 'localhost', 'port': 8080, 'debug': True}
  13. print(f"Host: {get_config('host')}")

    32print(f"Host: {get_config('host')}")33print(f"Port: {get_config('port')}")34print(f"Debug: {get_config('debug')}")
    outputHost: localhost
  14. print(f"Port: {get_config('port')}")

    32print(f"Host: {get_config('host')}")33print(f"Port: {get_config('port')}")34print(f"Debug: {get_config('debug')}")
    outputPort: 8080
  15. print(f"Debug: {get_config('debug')}")

    33print(f"Port: {get_config('port')}")34print(f"Debug: {get_config('debug')}")
    outputDebug: True
Union A type hint that accepts any of the specified types, useful when a value can legitimately be different types.

Callable Types

Use Callable to type function parameters that accept functions.

callable.py
Replay: real traced execution (multi-file project)
# Callable type for function parameters

from typing import Callable, List

def apply_operation(numbers: List[int], operation: Callable[[int], int]) -> List[int]:
    """Apply operation to each number"""
    return [operation(n) for n in numbers]

def filter_values(numbers: List[int], predicate: Callable[[int], bool]) -> List[int]:
    """Filter numbers using predicate function"""
    return [n for n in numbers if predicate(n)]

# Define operations
def double(x: int) -> int:
    return x * 2

def square(x: int) -> int:
    return x * x

def is_even(x: int) -> bool:
    return x % 2 == 0

# Usage
numbers = [1, 2, 3, 4, 5]

doubled = apply_operation(numbers, double)
print(f"Doubled: {doubled}")

squared = apply_operation(numbers, square)
print(f"Squared: {squared}")

evens = filter_values(numbers, is_even)
print(f"Even numbers: {evens}")

# Using lambda
odds = filter_values(numbers, lambda x: x % 2 != 0)
print(f"Odd numbers: {odds}")
  1. numbers ← [1, 2, 3, 4, 5]

    23# Usage24numbers→ [1, 2, 3, 4, 5] = [1, 2, 3, 4, 5]2526doubled = apply_operation(numbers[1, 2, 3, 4, 5], double⟨function double A⟩)27print(f"Doubled: {doubled}")
  2. def apply_operation(numbers: List[int], operation: Callable[[int], int…

    pass 1 of 2
    5def apply_operation(numbers[1, 2, 3, 4, 5]: List[int], operation⟨function double A⟩: Callable[[int], int]) -> List[int]:6    """Apply operation to each number"""7    return [operation(n) for n in numbers[1, 2, 3, 4, 5]]
  3. def double(x: int) -> int:

    pass 1 of 5
    13# Define operations14def double(x1: int) -> int:15    return x1 * 2
    All 5 passes — pass 1 is the card above
    passx
    11
    22
    33
    44
    55
  4. doubled ← [2, 4, 6, 8, 10]

    26doubled→ [2, 4, 6, 8, 10] = apply_operation(numbers[1, 2, 3, 4, 5], double⟨function double A⟩)27print(f"Doubled: {doubled[2, 4, 6, 8, 10]}")2829squared = apply_operation(numbers[1, 2, 3, 4, 5], square⟨function square B⟩)30print(f"Squared: {squared}")
    outputDoubled: [2, 4, 6, 8, 10]
  5. def apply_operation(numbers: List[int], operation: Callable[[int], int…

    pass 2 of 2
    5def apply_operation(numbers[1, 2, 3, 4, 5]: List[int], operation⟨function square B⟩: Callable[[int], int]) -> List[int]:6    """Apply operation to each number"""7    return [operation(n) for n in numbers[1, 2, 3, 4, 5]]
  6. def square(x: int) -> int:

    pass 1 of 5
    17def square(x1: int) -> int:18    return x1 * x
    All 5 passes — pass 1 is the card above
    passx
    11
    22
    33
    44
    55
  7. squared ← [1, 4, 9, 16, 25]

    29squared→ [1, 4, 9, 16, 25] = apply_operation(numbers[1, 2, 3, 4, 5], square⟨function square B⟩)30print(f"Squared: {squared[1, 4, 9, 16, 25]}")3132evens = filter_values(numbers[1, 2, 3, 4, 5], is_even⟨function is_even C⟩)33print(f"Even numbers: {evens}")
    outputSquared: [1, 4, 9, 16, 25]
  8. def filter_values(numbers: List[int], predicate: Callable[[int], bool]…

    pass 1 of 2
    9def filter_values(numbers[1, 2, 3, 4, 5]: List[int], predicate⟨function is_even C⟩: Callable[[int], bool]) -> List[int]:10    """Filter numbers using predicate function"""11    return [n for n in numbers[1, 2, 3, 4, 5] if predicate(n)]
  9. def is_even(x: int) -> bool:

    pass 1 of 5
    20def is_even(x1: int) -> bool:21    return x1 % 2 == 0
    All 5 passes — pass 1 is the card above
    passx
    11
    22
    33
    44
    55
  10. evens ← [2, 4]

    32evens→ [2, 4] = filter_values(numbers[1, 2, 3, 4, 5], is_even⟨function is_even C⟩)33print(f"Even numbers: {evens[2, 4]}")3435# Using lambda36odds = filter_values(numbers[1, 2, 3, 4, 5], lambda x: x % 2 != 0)37print(f"Odd numbers: {odds}")
    outputEven numbers: [2, 4]
  11. def filter_values(numbers: List[int], predicate: Callable[[int], bool]…

    pass 2 of 2
    9def filter_values(numbers[1, 2, 3, 4, 5]: List[int], predicate<function <lambda> at ⟨addr D⟩>: Callable[[int], bool]) -> List[int]:10    """Filter numbers using predicate function"""11    return [n for n in numbers[1, 2, 3, 4, 5] if predicate(n)]
  12. odds ← [1, 3, 5]

    35# Using lambda36odds→ [1, 3, 5] = filter_values(numbers[1, 2, 3, 4, 5], lambda x: x % 2 != 0)37print(f"Odd numbers: {odds[1, 3, 5]}")
    outputOdd numbers: [1, 3, 5]
Callable A type hint for function parameters or variables that hold callable objects, specifying the expected parameter types and return type.

Generic Types

Use TypeVar and Generic to create type-safe generic functions and classes.

generics.py
Replay: real traced execution (multi-file project)
# Generic types with TypeVar

from typing import TypeVar, List, Generic

T = TypeVar('T')

def get_first(items: List[T]) -> T:
    """Get first item from list, preserving type"""
    return items[0]

def get_last(items: List[T]) -> T:
    """Get last item from list, preserving type"""
    return items[-1]

# Generic class
class Stack(Generic[T]):
    def __init__(self):
        self._items: List[T] = []

    def push(self, item: T) -> None:
        self._items.append(item)

    def pop(self) -> T:
        return self._items.pop()

    def is_empty(self) -> bool:
        return len(self._items) == 0

# Usage with different types
numbers = [1, 2, 3, 4, 5]
print(f"First number: {get_first(numbers)}")
print(f"Last number: {get_last(numbers)}")

words = ["hello", "world", "python"]
print(f"First word: {get_first(words)}")
print(f"Last word: {get_last(words)}")

# Generic stack
int_stack: Stack[int] = Stack()
int_stack.push(10)
int_stack.push(20)
int_stack.push(30)
print(f"Popped: {int_stack.pop()}")

str_stack: Stack[str] = Stack()
str_stack.push("a")
str_stack.push("b")
print(f"Popped: {str_stack.pop()}")
  1. T ← ~T, numbers ← [1, 2, 3, 4, 5]

    5T→ ~T = TypeVar('T')67def get_first(items: List[T]) -> T:8    """Get first item from list, preserving type"""9    return items[0]1011def get_last(items: List[T]) -> T:12    """Get last item from list, preserving type"""13    return items[-1]1415# Generic class16class Stack(Generic[T]):17    def __init__(self):18        self._items: List[T] = []19    20    def push(self, item: T) -> None:21        self._items.append(item)22    23    def pop(self) -> T:24        return self._items.pop()25    26    def is_empty(self) -> bool:27        return len(self._items) == 02829# Usage with different types30numbers→ [1, 2, 3, 4, 5] = [1, 2, 3, 4, 5]31print(f"First number: {get_first(numbers[1, 2, 3, 4, 5])}")32print(f"Last number: {get_last(numbers)}")
  2. def get_first(items: List[T]) -> T:

    pass 1 of 2
    7def get_first(items[1, 2, 3, 4, 5]: List[T]) -> T:8    """Get first item from list, preserving type"""9    return items[0]1
  3. print(f"First number: {get_first(numbers)}")

    30numbers = [1, 2, 3, 4, 5]31print(f"First number: {get_first(numbers[1, 2, 3, 4, 5])}")32print(f"Last number: {get_last(numbers[1, 2, 3, 4, 5])}")
    outputFirst number: 1
  4. def get_last(items: List[T]) -> T:

    pass 1 of 2
    11def get_last(items[1, 2, 3, 4, 5]: List[T]) -> T:12    """Get last item from list, preserving type"""13    return items[-1]5
  5. words ← ['hello', 'world', 'python']

    31print(f"First number: {get_first(numbers)}")32print(f"Last number: {get_last(numbers[1, 2, 3, 4, 5])}")3334words→ ['hello', 'world', 'python'] = ["hello", "world", "python"]35print(f"First word: {get_first(words['hello', 'world', 'python'])}")36print(f"Last word: {get_last(words)}")
    outputLast number: 5
  6. def get_first(items: List[T]) -> T:

    pass 2 of 2
    7def get_first(items['hello', 'world', 'python']: List[T]) -> T:8    """Get first item from list, preserving type"""9    return items[0]hello
  7. print(f"First word: {get_first(words)}")

    34words = ["hello", "world", "python"]35print(f"First word: {get_first(words['hello', 'world', 'python'])}")36print(f"Last word: {get_last(words['hello', 'world', 'python'])}")
    outputFirst word: hello
  8. def get_last(items: List[T]) -> T:

    pass 2 of 2
    11def get_last(items['hello', 'world', 'python']: List[T]) -> T:12    """Get last item from list, preserving type"""13    return items[-1]python
  9. print(f"Last word: {get_last(words)}")

    35print(f"First word: {get_first(words)}")36print(f"Last word: {get_last(words['hello', 'world', 'python'])}")3738# Generic stack39int_stack: Stack[int] = Stack()40int_stack.push(10)
    outputLast word: python
  10. self._items ← []

    pass 1 of 2
    16class Stack(Generic[T]):17    def __init__(self⟨Stack A⟩):18        self._items→ []: List[T] = []
  11. int_stack ← ⟨Stack A⟩

    38# Generic stack39int_stack→ ⟨Stack A⟩: Stack[int] = Stack()40int_stack⟨Stack A⟩.push(10)41int_stack.push(20)
  12. self._items ← [10]

    pass 1 of 5
    20def push(self⟨Stack A⟩, item10: T) -> None:21    self._items→ [10].append(item10)
    All 5 passes — pass 1 is the card above
    passselfitemself._items
    1⟨Stack A⟩10[] [10]
    2⟨Stack A⟩20[10] [10, 20]
    3⟨Stack A⟩30[10, 20] [10, 20, 30]
    4⟨Stack B⟩a[] ['a']
    5⟨Stack B⟩b['a'] ['a', 'b']
  13. int_stack.push(10)

    39int_stack: Stack[int] = Stack()40int_stack⟨Stack A⟩.push(10)41int_stack⟨Stack A⟩.push(20)42int_stack.push(30)
  14. int_stack.push(20)

    40int_stack.push(10)41int_stack⟨Stack A⟩.push(20)42int_stack⟨Stack A⟩.push(30)43print(f"Popped: {int_stack.pop()}")
  15. int_stack.push(30)

    41int_stack.push(20)42int_stack⟨Stack A⟩.push(30)43print(f"Popped: {int_stack⟨Stack A⟩.pop()}")
  16. def pop(self) -> T:

    pass 1 of 2
    23def pop(self⟨Stack A⟩) -> T:24    return self._items[10, 20, 30].pop()
  17. print(f"Popped: {int_stack.pop()}")

    42int_stack.push(30)43print(f"Popped: {int_stack⟨Stack A⟩.pop()}")4445str_stack: Stack[str] = Stack()46str_stack.push("a")
    outputPopped: 30
  18. self._items ← []

    pass 2 of 2
    16class Stack(Generic[T]):17    def __init__(self⟨Stack B⟩):18        self._items→ []: List[T] = []
  19. str_stack ← ⟨Stack B⟩

    45str_stack→ ⟨Stack B⟩: Stack[str] = Stack()46str_stack⟨Stack B⟩.push("a")47str_stack.push("b")
  20. str_stack.push("a")

    45str_stack: Stack[str] = Stack()46str_stack⟨Stack B⟩.push("a")47str_stack⟨Stack B⟩.push("b")48print(f"Popped: {str_stack.pop()}")
  21. str_stack.push("b")

    46str_stack.push("a")47str_stack⟨Stack B⟩.push("b")48print(f"Popped: {str_stack⟨Stack B⟩.pop()}")
  22. def pop(self) -> T:

    pass 2 of 2
    23def pop(self⟨Stack B⟩) -> T:24    return self._items['a', 'b'].pop()
  23. print(f"Popped: {str_stack.pop()}")

    47str_stack.push("b")48print(f"Popped: {str_stack⟨Stack B⟩.pop()}")
    outputPopped: b

@seealso dataclass_intro "Dataclasses with type hints" @seealso namedtuple_intro "Typed NamedTuples"

Generic A base class for creating generic types that work with any type while maintaining type safety, using TypeVar for type parameters.