Introduccion a las anotaciones de tipo
Python tiene tipado dinamico, pero desde Python 3.5, puedes agregar anotaciones de tipo (tambien llamadas type hints) a tu codigo. Las anotaciones de tipo no afectan el comportamiento en tiempo de ejecucion — son usadas por herramientas como mypy, IDEs y linters para detectar errores antes de que se ejecute tu codigo. Tambien sirven como excelente documentacion.
# Basic type hints
name: str = "Alice"
age: int = 30
height: float = 5.7
is_active: bool = True
# Function type hints
def greet(name: str) -> str:
return f"Hello, {name}!"
def add(a: int, b: int) -> int:
return a + b
# None return type
def log_message(message: str) -> None:
print(f"[LOG] {message}")
# Optional parameters
def find_user(user_id: int, active_only: bool = True) -> str:
return f"User {user_id}"
Tipos de coleccion
# Python 3.9+ - use built-in types directly
numbers: list[int] = [1, 2, 3]
coordinates: tuple[float, float] = (3.14, 2.72)
unique_ids: set[str] = {"abc", "def"}
scores: dict[str, int] = {"Alice": 95, "Bob": 87}
# Variable-length tuples
values: tuple[int, ...] = (1, 2, 3, 4, 5)
# Nested types
matrix: list[list[int]] = [[1, 2], [3, 4]]
users: dict[str, list[str]] = {
"admins": ["Alice", "Bob"],
"users": ["Charlie", "Diana"],
}
# Python 3.8 and earlier - use typing module
from typing import List, Dict, Tuple, Set
numbers: List[int] = [1, 2, 3] # Legacy syntax
Union, Optional y tipos avanzados
from typing import Optional, Union
# Union types - can be one of several types
# Python 3.10+ syntax
def process(value: int | str) -> str:
return str(value)
# Pre-3.10 syntax
def process_old(value: Union[int, str]) -> str:
return str(value)
# Optional - shorthand for Union[X, None]
def find_user(user_id: int) -> Optional[dict]:
"""Returns user dict or None if not found."""
if user_id == 1:
return {"id": 1, "name": "Alice"}
return None
# Python 3.10+ equivalent
def find_user_new(user_id: int) -> dict | None:
pass
# Literal types - restrict to specific values
from typing import Literal
def set_color(color: Literal["red", "green", "blue"]) -> None:
print(f"Color set to {color}")
set_color("red") # OK
# set_color("purple") # mypy error!
# TypeAlias for complex types
type UserId = int
type UserMap = dict[UserId, str]
# Pre-3.12 syntax:
from typing import TypeAlias
UserId: TypeAlias = int
Tipos Callable y Protocol
from typing import Callable, Protocol
# Callable - type hint for functions
def apply_operation(
values: list[int],
operation: Callable[[int], int]
) -> list[int]:
return [operation(v) for v in values]
result = apply_operation([1, 2, 3], lambda x: x ** 2)
print(result) # [1, 4, 9]
# Callable with keyword args
Handler = Callable[[str, int], bool]
# Protocol - structural subtyping (duck typing with types)
class Renderable(Protocol):
def render(self) -> str: ...
class HTMLElement:
def render(self) -> str:
return "Hello"
class MarkdownText:
def render(self) -> str:
return "# Hello"
def display(item: Renderable) -> None:
"""Accepts any object with a render() -> str method."""
print(item.render())
display(HTMLElement()) # Works
display(MarkdownText()) # Works - no inheritance needed!
Generics
from typing import TypeVar, Generic
# TypeVar for generic functions
T = TypeVar("T")
def first(items: list[T]) -> T:
return items[0]
x: int = first([1, 2, 3]) # T is inferred as int
y: str = first(["a", "b", "c"]) # T is inferred as str
# Generic classes
class Stack(Generic[T]):
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
def peek(self) -> T:
return self._items[-1]
def is_empty(self) -> bool:
return len(self._items) == 0
int_stack: Stack[int] = Stack()
int_stack.push(42)
# int_stack.push("hello") # mypy error!
# Python 3.12+ syntax (simpler!)
def first_new[T](items: list[T]) -> T:
return items[0]
class Stack_new[T]:
def __init__(self) -> None:
self._items: list[T] = []
Ejecutar mypy
# Install mypy
# pip install mypy
# Run type checking
# mypy myfile.py
# mypy src/
# Configuration in pyproject.toml
# [tool.mypy]
# python_version = "3.12"
# strict = true
# warn_return_any = true
# warn_unused_configs = true
# disallow_untyped_defs = true
Puntos clave
- Las anotaciones de tipo son opcionales: No afectan el tiempo de ejecucion pero mejoran la calidad del codigo
- Usa sintaxis moderna: list[int] sobre List[int], int | None sobre Optional[int]
- Protocol para duck typing: Define interfaces sin herencia
- Ejecuta mypy en CI: Detecta errores de tipo antes de que lleguen a produccion