En Python, un tipo de dato define el tipo de valor que una variable puede almacenar. Esto ayuda a Python a saber qué operaciones se pueden realizar con los datos y a prevenir errores.
- Enteros (
int
): Números enteros sin parte decimal.edad = 25
- Flotantes (
float
): Números con parte decimal.precio = 19.99
Las cadenas de texto son secuencias de caracteres.
nombre = "Juan"
Los booleanos representan valores de verdad: True
o False
.
es_mayor = True
Las listas son colecciones ordenadas y mutables de elementos.
frutas = ["manzana", "banana", "cereza"]
Las tuplas son colecciones ordenadas e inmutables de elementos.
coordenadas = (40.7128, -74.0060)
Los conjuntos son colecciones desordenadas de elementos únicos.
colores = {"rojo", "verde", "azul"}
Los diccionarios son colecciones de pares clave-valor.
persona = {
"nombre": "Ana",
"edad": 30,
"ciudad": "Madrid"
}
Puedes usar la función type()
incluida en python
para verificar el tipo de una variable.
numero = 42
print(type(numero)) # Output: <class 'int'>
A veces, necesitas convertir el tipo de una variable a otro tipo. Esto se llama conversión de tipos o type casting.
- De entero a cadena:
edad = 25 edad_str = str(edad)
- De cadena a entero:
edad_str = "25" edad = int(edad_str)
- De flotante a entero:
precio = 19.99 precio_entero = int(precio) # Output: 19
Los tipos genéricos permiten definir clases y funciones que pueden trabajar con cualquier tipo de dato. Esto es útil para crear estructuras de datos y algoritmos genéricos.
from typing import TypeVar
T = TypeVar('T')
def invertir_lista(lista: list[T]) -> list[T]:
return lista[::-1]
print(invertir_lista([1, 2, 3])) # Output: [3, 2, 1]
print(invertir_lista(["a", "b", "c"])) # Output: ["c", "b", "a"]
- TypeVar define un parámetro de tipo genérico T.
- invertir_lista toma una lista de cualquier tipo T y devuelve una lista del mismo tipo T
Union
permite definir una variable que puede ser de más de un tipo. En Python 3.10, se introdujo el operador |
como una forma más concisa de expresar esto.
from typing import Union
def procesar_datos(data: Union[int, str]) -> str:
if isinstance(data, int):
return f"Número procesado: {data}"
elif isinstance(data, str):
return f"Texto procesado: {data}"
print(procesar_datos(42)) # Output: Procesado número: 42
print(procesar_datos("hola")) # Output: Procesado texto: hola
def procesar_datos_v2(data: int | str) -> str:
if isinstance(data, int):
return f"Número procesado: {data}"
elif isinstance(data, str):
return f"Texto procesado: {data}"
print(procesar_datos_v2(42)) # Output: Procesado número: 42
print(procesar_datos_v2("hola")) # Output: Procesado texto: hola
Union[int, str]
y int | str
indican que la variable data puede ser un entero o una cadena.
Annotated
permite agregar metadatos a los tipos, lo cual es útil para validación y documentación.
from typing import Annotated
from pydantic import BaseModel, Field
class Usuario(BaseModel):
nombre: Annotated[str, Field(max_length=30)]
edad: Annotated[int, Field(gt=0)]
usuario = Usuario(nombre="Juan", edad=25)
print(usuario) # Output: nombre='Juan' edad=25
Annotated
permite agregar restricciones y descripciones a los campos, como max_length
y gt
.
Los tipos literales permiten restringir una variable a un conjunto fijo de valores.
from typing import Literal
def obtener_estado(estado: Literal["activo", "inactivo", "pendiente"]) -> str:
if estado == "activo":
return "El usuario está activo"
elif estado == "inactivo":
return "El usuario está inactivo"
elif estado == "pendiente":
return "El estado del usuario está pendiente"
print(obtener_estado("activo")) # Output: El usuario está activo
print(obtener_estado("inactivo")) # Output: El usuario está inactivo
print(obtener_estado("pendiente")) # Output: El estado del usuario está pendiente
Literal
restringe el valor del parámetro estado a los valores específicos "activo", "inactivo" y "pendiente".
Los alias de tipos permiten crear nombres más significativos para tipos complejos.
from typing import TypeAlias
UsuarioID: TypeAlias = int
def obtener_usuario(usuario_id: UsuarioID) -> str:
return f"Obteniendo usuario con ID: {usuario_id}"
print(obtener_usuario(101)) # Output: Obteniendo usuario con ID: 101
TypeAlias
define UsuarioID
como un alias para int
, lo que hace el código más legible y auto-documentado.
Los protocolos permiten definir tipos que deben cumplir con una estructura específica, sin herencia explícita.
from typing import Protocol
class Describible(Protocol):
def describir(self) -> str:
...
class Producto:
def describir(self) -> str:
return "Este es un producto"
class Servicio:
def describir(self) -> str:
return "Este es un servicio"
def imprimir_descripcion(item: Describible) -> None:
print(item.describir())
producto = Producto()
servicio = Servicio()
imprimir_descripcion(producto) # Output: Este es un producto
imprimir_descripcion(servicio) # Output: Este es un servicio
Protocol
define una estructura que debe ser implementada por cualquier clase que use Describible
.
Puedes encontrar algunos ejemplos básicos de implementaciones de tipos en el script fastapi_ejemplos.py
Entender los tipos de datos es fundamental para escribir programas efectivos en Python. Estos conceptos básicos te ayudarán a gestionar y manipular datos de manera eficiente, adicionalmente te ayudaran a evitar cometer errores de tipos con ayuda de tu IDE favorito.
Ya que has llegado al módulo extra de tipos, que tal si practicamos todo lo aprendido implementando los tipos que has visto en los módulos del 3 al 9, reescribe todos los ejemplos implementando las validaciones de tipos que más crees convenientes!
- Ejemplo: en el módulo 4, trata de agregar
Annotated
en todos los parámetros del servicio/menu_items/{item_id}
, para indicar el numero de caracteres en un campo string o indicar que un numero debe ser positivo en/docs
- Ejemplo: En el módulo 5, implementar un Literal dentro de la clase
Reservation
de pydantic, para limitar el camponame
solo pueda aceptar los nombres de["pepe", "carlos", "juan"]
- Continua implementando más tipos en todos los módulos restantes para que sigas practicando!