Type Casting, Type Inspection, and Annotations
Type Casting, Type Inspection, and Annotations
After reading this chapter, you will master Python’s strong typing model, safely perform type conversions, inspect object types at runtime, and write modern Python 3.14 type annotations.
Mental model
Python is strongly typed and dynamically typed: - Strongly typed: Python does not implicitly coerce incompatible types (e.g. adding a string and an integer raises TypeError). - Dynamically typed: Types belong to values, not to variable names. A name can point to an integer at one moment and a string the next.
Strong Typing Enforcement:
"5" + 2 ──▶ TypeError: can only concatenate str (not "int") to str
Explicit Conversion:
int("5") + 2 ──▶ 7 (Integer addition)
"5" + str(2) ──▶ "52" (String concatenation)
At runtime, isinstance() checks if an object belongs to a class or any of its subclasses, traversing the inheritance tree.
Minimal example
Save as type_inspect_demo.py:
# type_inspect_demo.py
def process_metric(raw_value: str | int | float) -> float:
# 1. Type inspection with isinstance
if isinstance(raw_value, (int, float)):
return float(raw_value)
# 2. Explicit type casting
try:
return float(raw_value)
except ValueError as err:
raise ValueError(f"Cannot cast {raw_value!r} to float") from err
def main() -> None:
samples = [42, "128.5", 3.14159, "invalid_num"]
for sample in samples:
try:
result = process_metric(sample)
print(f"Sample: {sample!r:15s} -> Processed float: {result}")
except ValueError as err:
print(f"Sample: {sample!r:15s} -> Error: {err}")
if __name__ == "__main__":
main()Run via uv run python type_inspect_demo.py:
Sample: 42 -> Processed float: 42.0
Sample: '128.5' -> Processed float: 128.5
Sample: 3.14159 -> Processed float: 3.14159
Sample: 'invalid_num' -> Error: Cannot cast 'invalid_num' to float
Worked examples
Case 1: Checking types: isinstance vs type()
isinstance() respects class inheritance hierarchies, while type(obj) is Class requires an exact type match.
# isinstance_vs_type.py
class Animal:
pass
class Dog(Animal):
pass
def verify_types() -> None:
d = Dog()
print(f"type(d) is Dog : {type(d) is Dog}")
print(f"type(d) is Animal : {type(d) is Animal} (False: ignores inheritance)")
print(f"isinstance(d, Animal): {isinstance(d, Animal)} (True: recognizes subclass)")
# bool is a subclass of int!
print(f"isinstance(True, int): {isinstance(True, int)} (True!)")
print(f"type(True) is int : {type(True) is int} (False!)")
if __name__ == "__main__":
verify_types()Run:
uv run python isinstance_vs_type.pyCase 2: Modern Python 3.14 type annotations
Type annotations provide documentation and allow static type checkers like mypy and pyright to find bugs before runtime:
# typed_service.py
def calculate_throughput(requests: int, duration_seconds: float) -> float:
if duration_seconds <= 0:
raise ValueError("Duration must be positive")
return requests / duration_seconds
def lookup_node(node_id: str) -> str | None:
nodes = {"node-1": "10.0.0.1", "node-2": "10.0.0.2"}
return nodes.get(node_id)
if __name__ == "__main__":
tps = calculate_throughput(10_000, 4.5)
print(f"Throughput: {tps:.2f} req/s")
print(f"Lookup: {lookup_node('node-1')}")Run:
uv run python typed_service.pyPitfalls
Pitfall 1: Using type(x) == T instead of isinstance()
Checking type(x) == int will return False if someone passes a subclass or specialized numerical type. Always prefer isinstance(x, int).
Pitfall 2: Assuming type annotations enforce runtime checks
Python does not validate type hints at runtime by default. If you pass a string to a function annotated with x: int, Python executes it without error until an incompatible operation occurs. Runtime validation requires libraries like Pydantic.
Exercises
- Write a function
safe_int_cast(val: str, default: int = 0) -> intthat parses an integer string and returnsdefaultif parsing fails. - Given a mixed list
items = [1, "two", 3.0, [4], (5,), {"six": 6}], write a script that separates elements into a dictionary grouped by type name. - Run
mypyorruff checkon a script containing a deliberate type annotation mismatch to observe static analysis feedback.
Further reading
- PEP 484: Type Hints.
- PEP 604: Allow-writing union types as X | Y.
- Python Standard Library:
typingmodule.