Effective Use of Python Type Hints: Basics, Optional, Collections, Unions, and Any
This article explains how to apply Python type hints—including basic types, the modern pipe syntax for optional values, collection generics, union types, and the flexible Any type—through clear examples and best‑practice guidance to make code more readable and safer.
In the previous article we discussed why adding type hints to Python code is beneficial; this piece shows how to use them effectively with practical examples.
Basic Types
Type hints start with the fundamental Python types, which immediately clarify a function's expected inputs and outputs.
<code>def calculate_age(birth_year: int) -> int:
return 2024 - birth_year
def is_valid_username(name: str) -> bool:
return len(name) >= 3 and name.isalnum()
def get_price(amount: float, discount: float = 0.0) -> float:
return amount * (1 - discount)</code>These simple annotations tell other developers exactly what data each function expects and returns.
Optional
When a variable may be None , earlier Python versions used Optional from the typing module. Since Python 3.10, the more concise pipe syntax ( | ) can be used.
<code>from typing import Optional
# Python 3.9 style
def get_user(user_id: Optional[int]) -> Optional[dict]:
if user_id is None:
return None
return {"id": user_id, "name": "John"}
# Python 3.10+ style
def get_user(user_id: int | None) -> dict | None:
if user_id is None:
return None
return {"id": user_id, "name": "John"}</code>The pipe syntax is a direct shorthand for Union[T, None] and is especially useful for database queries that may miss a record, API responses with missing data, user input that can be empty, or optional configuration values.
Collections
When using list , dict , set , and other collection types, type hints can specify the types of the elements they contain.
<code>def process_orders(orders: list[dict[str, float]]) -> float:
return sum(order['amount'] for order in orders)
def get_user_stats(user_id: int) -> tuple[str, int, float]:
# Returns (username, total_orders, average_order_value)
return ('user123', 50, 29.99)
def find_user(email: str) -> dict[str, any] | None:
# Returns None if user not found
return database.get_user(email)</code>These annotations create a clear contract, indicating that a list of dictionaries with string keys and float values is expected, for example.
Unions
Union types describe values that may be one of several types.
<code>from typing import Union
# Python 3.10+ syntax
def process_identifier(id: int | str) -> str:
# Can handle both numeric IDs and string IDs
return str(id).upper()
# Pre‑3.10 syntax
def process_identifier_old(id: Union[int, str]) -> str:
return str(id).upper()</code>Any
When maximum flexibility is needed, Any can be used, but it disables type checking for that value.
<code>from typing import Any
def store_metadata(key: str, value: Any) -> None:
# Can store any type of value
database.set(key, value)</code>Use Any sparingly, as it effectively turns off static type checking.
These examples cover the basic usage of Python type hints; try incorporating them into your next codebase.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.