Modern Python Types
Typing Module Introduction
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
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)}")
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}")def add(a: int, b: int) -> int:
3def add(a10: int, b20: int) -> int:4 return a10 + b20result ← 30
20# Using the functions21result→ 30 = add(10, 20)22print(f"10 + 20 = {result30}")2324message = greet("Bob")25print(message)output10 + 20 = 30def greet(name: str) -> str:
6def greet(nameBob: str) -> str:7 return f"Hello, {nameBob}!"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!def is_adult(age: int) -> bool:
pass 1 of 29def is_adult(age25: int) -> bool:10 return age25 >= 18print(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? Truedef is_adult(age: int) -> bool:
pass 2 of 29def is_adult(age15: int) -> bool:10 return age15 >= 18print(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
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}")def add(a: int, b: int) -> int:
3def add(a10: int, b20: int) -> int:4 return a10 + b20result ← 30
18# Using the functions19result→ 30 = add(10, 20)20print(f"10 + 20 = {result30}")2122message = greet("Bob")23print(message)output10 + 20 = 30def greet(name: str) -> str:
6def greet(nameBob: str) -> str:7 return f"Hello, {nameBob}!"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!def is_adult(age: int) -> bool:
pass 1 of 29def is_adult(age25: int) -> bool:10 return age25 >= 18print(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? Truedef is_adult(age: int) -> bool:
pass 2 of 29def is_adult(age15: int) -> bool:10 return age15 >= 18print(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
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}")def add(a: int, b: int) -> int:
3def add(a10: int, b20: int) -> int:4 return a10 + b20result ← 30
18# Using the functions19result→ 30 = add(10, 20)20print(f"10 + 20 = {result30}")2122message = greet("Bob")23print(message)output10 + 20 = 30def greet(name: str) -> str:
6def greet(nameBob: str) -> str:7 return f"Hello, {nameBob}!"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!def is_adult(age: int) -> bool:
pass 1 of 29def is_adult(age25: int) -> bool:10 return age25 >= 18print(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? Truedef is_adult(age: int) -> bool:
pass 2 of 29def is_adult(age15: int) -> bool:10 return age15 >= 18print(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
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}")def add(a: int, b: int) -> int:
3def add(a10: int, b20: int) -> int:4 return a10 + b20result ← 30
18# Using the functions19result→ 30 = add(10, 20)20print(f"10 + 20 = {result30}")2122message = greet("Bob")23print(message)output10 + 20 = 30def greet(name: str) -> str:
6def greet(nameBob: str) -> str:7 return f"Hello, {nameBob}!"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!def is_adult(age: int) -> bool:
pass 1 of 29def is_adult(age25: int) -> bool:10 return age25 >= 18print(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? Truedef is_adult(age: int) -> bool:
pass 2 of 29def is_adult(age15: int) -> bool:10 return age15 >= 18print(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
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}")def add(a: int, b: int) -> int:
3def add(a10: int, b20: int) -> int:4 return a10 + b20result ← 30
18# Using the functions19result→ 30 = add(10, 20)20print(f"10 + 20 = {result30}")2122message = greet("Bob")23print(message)output10 + 20 = 30def greet(name: str) -> str:
6def greet(nameBob: str) -> str:7 return f"Hello, {nameBob}!"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!def is_adult(age: int) -> bool:
pass 1 of 29def is_adult(age25: int) -> bool:10 return age25 >= 18print(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? Truedef is_adult(age: int) -> bool:
pass 2 of 29def is_adult(age15: int) -> bool:10 return age15 >= 18print(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})")
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}")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])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: 15scores ← {'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}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])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}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}")
user = find_user(2)
16# Usage17user = find_user(2)18if user is not None:users ← {1: 'Alice', 2: 'Bob', 3: 'Charlie'}
pass 1 of 25def 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)user ← Bob
16# Usage17user→ Bob = find_user(2)18if user is not None:if user is not None:
17user = find_user(2)18if userBob is not None:19 print(f"Found user: {userBob}")20else:outputFound user: Bobuser = find_user(999)
23user = find_user(999)24if user is not None:users ← {1: 'Alice', 2: 'Bob', 3: 'Charlie'}
pass 2 of 25def 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)user ← None
23user→ None = find_user(999)24if user is not None:else:
24if user is not None:25 print(f"Found user: {user}")26else:27 print("User not found")outputUser not foundresult = divide(10, 2)
29# Division30result = divide(10, 2)31print(f"10 / 2 = {result}")def divide(a: float, b: float) -> Optional[float]:
pass 1 of 210def 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 / b2result ← 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.0def divide(a: float, b: float) -> Optional[float]:
pass 2 of 210def divide(a10: float, b0: float) -> Optional[float]:11 """Returns result or None if division by zero"""12 if b == 0:if b == 0:
11"""Returns result or None if division by zero"""12if b0 == 0:13 return None14return a / bresult ← 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')}")
print(format_value(42))
15# Usage16print(format_value(42))17print(format_value(3.14))def format_value(value: Union[int, float, str]) -> str:
pass 1 of 35def 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 pass value1 42 2 3.14 3 hello if isinstance(value, (int, float)):
pass 1 of 26"""Format different types of values"""7if isinstance(value42, (int, float)):8 return f"Number: {value42}"9return f"Text: {value}"print(format_value(42))
15# Usage16print(format_value(42))17print(format_value(3.14))18print(format_value("hello"))outputNumber: 42if isinstance(value, (int, float)):
pass 2 of 26"""Format different types of values"""7if isinstance(value3.14, (int, float)):8 return f"Number: {value3.14}"9return f"Text: {value}"print(format_value(3.14))
16print(format_value(42))17print(format_value(3.14))18print(format_value("hello"))outputNumber: 3.14print(format_value("hello"))
17print(format_value(3.14))18print(format_value("hello"))1920print(process_id(1001))21print(process_id("ABC123"))outputText: hellodef process_id(id_value: Union[int, str]) -> str:
pass 1 of 211def process_id(id_value1001: Union[int, str]) -> str:12 """Process ID that can be int or string"""13 return f"ID-{id_value1001}"print(process_id(1001))
20print(process_id(1001))21print(process_id("ABC123"))outputID-1001def process_id(id_value: Union[int, str]) -> str:
pass 2 of 211def process_id(id_valueABC123: Union[int, str]) -> str:12 """Process ID that can be int or string"""13 return f"ID-{id_valueABC123}"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-ABC123config ← {'host': 'localhost', 'port': 8080, 'debug': True}
pass 1 of 323# 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 pass keyconfig1 host {'host': 'localhost', 'port': 8080, 'debug': True} 2 port {'host': 'localhost', 'port': 8080, 'debug': True} 3 debug {'host': 'localhost', 'port': 8080, 'debug': True} 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: localhostprint(f"Port: {get_config('port')}")
32print(f"Host: {get_config('host')}")33print(f"Port: {get_config('port')}")34print(f"Debug: {get_config('debug')}")outputPort: 8080print(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}")
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}")def apply_operation(numbers: List[int], operation: Callable[[int], int…
pass 1 of 25def 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]]def double(x: int) -> int:
pass 1 of 513# Define operations14def double(x1: int) -> int:15 return x1 * 2All 5 passes — pass 1 is the card above pass x1 1 2 2 3 3 4 4 5 5 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]def apply_operation(numbers: List[int], operation: Callable[[int], int…
pass 2 of 25def 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]]def square(x: int) -> int:
pass 1 of 517def square(x1: int) -> int:18 return x1 * xAll 5 passes — pass 1 is the card above pass x1 1 2 2 3 3 4 4 5 5 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]def filter_values(numbers: List[int], predicate: Callable[[int], bool]…
pass 1 of 29def 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)]def is_even(x: int) -> bool:
pass 1 of 520def is_even(x1: int) -> bool:21 return x1 % 2 == 0All 5 passes — pass 1 is the card above pass x1 1 2 2 3 3 4 4 5 5 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]def filter_values(numbers: List[int], predicate: Callable[[int], bool]…
pass 2 of 29def 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)]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()}")
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)}")def get_first(items: List[T]) -> T:
pass 1 of 27def get_first(items[1, 2, 3, 4, 5]: List[T]) -> T:8 """Get first item from list, preserving type"""9 return items[0]1print(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: 1def get_last(items: List[T]) -> T:
pass 1 of 211def get_last(items[1, 2, 3, 4, 5]: List[T]) -> T:12 """Get last item from list, preserving type"""13 return items[-1]5words ← ['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: 5def get_first(items: List[T]) -> T:
pass 2 of 27def get_first(items['hello', 'world', 'python']: List[T]) -> T:8 """Get first item from list, preserving type"""9 return items[0]helloprint(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: hellodef get_last(items: List[T]) -> T:
pass 2 of 211def get_last(items['hello', 'world', 'python']: List[T]) -> T:12 """Get last item from list, preserving type"""13 return items[-1]pythonprint(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: pythonself._items ← []
pass 1 of 216class Stack(Generic[T]):17 def __init__(self⟨Stack A⟩):18 self._items→ []: List[T] = []int_stack ← ⟨Stack A⟩
38# Generic stack39int_stack→ ⟨Stack A⟩: Stack[int] = Stack()40int_stack⟨Stack A⟩.push(10)41int_stack.push(20)self._items ← [10]
pass 1 of 520def push(self⟨Stack A⟩, item10: T) -> None:21 self._items→ [10].append(item10)All 5 passes — pass 1 is the card above pass selfitemself._items1 ⟨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'] 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)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()}")int_stack.push(30)
41int_stack.push(20)42int_stack⟨Stack A⟩.push(30)43print(f"Popped: {int_stack⟨Stack A⟩.pop()}")def pop(self) -> T:
pass 1 of 223def pop(self⟨Stack A⟩) -> T:24 return self._items[10, 20, 30].pop()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: 30self._items ← []
pass 2 of 216class Stack(Generic[T]):17 def __init__(self⟨Stack B⟩):18 self._items→ []: List[T] = []str_stack ← ⟨Stack B⟩
45str_stack→ ⟨Stack B⟩: Stack[str] = Stack()46str_stack⟨Stack B⟩.push("a")47str_stack.push("b")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()}")str_stack.push("b")
46str_stack.push("a")47str_stack⟨Stack B⟩.push("b")48print(f"Popped: {str_stack⟨Stack B⟩.pop()}")def pop(self) -> T:
pass 2 of 223def pop(self⟨Stack B⟩) -> T:24 return self._items['a', 'b'].pop()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.