Introducción a los Tipos de Python¶
Python 3.6+ tiene soporte para "type hints" opcionales.
Estos type hints son una nueva sintaxis, desde Python 3.6+, que permite declarar el tipo de una variable.
Usando las declaraciones de tipos para tus variables, los editores y otras herramientas pueden proveerte un soporte mejor.
Este es solo un tutorial corto sobre los Python type hints. Solo cubre lo mínimo necesario para usarlos con FastAPI... realmente es muy poco lo que necesitas.
Todo FastAPI está basado en estos type hints, lo que le da muchas ventajas y beneficios.
Pero, así nunca uses FastAPI te beneficiarás de aprender un poco sobre los type hints.
Nota
Si eres un experto en Python y ya lo sabes todo sobre los type hints, salta al siguiente capítulo.
Motivación¶
Comencemos con un ejemplo simple:
def get_full_name(first_name, last_name):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
Llamar este programa nos muestra el siguiente output:
John Doe
La función hace lo siguiente:
- Toma un
first_name
y unlast_name
. - Convierte la primera letra de cada uno en una letra mayúscula con
title()
. - Las concatena con un espacio en la mitad.
def get_full_name(first_name, last_name):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
Edítalo¶
Es un programa muy simple.
Ahora, imagina que lo estás escribiendo desde cero.
En algún punto habrías comenzado con la definición de la función, tenías los parámetros listos...
Pero, luego tienes que llamar "ese método que convierte la primera letra en una mayúscula".
Era upper
? O era uppercase
? first_uppercase
? capitalize
?
Luego lo intentas con el viejo amigo de los programadores, el auto-completado del editor.
Escribes el primer parámetro de la función first_name
, luego un punto (.
) y luego presionas Ctrl+Space
para iniciar el auto-completado.
Tristemente, no obtienes nada útil:
Añade tipos¶
Vamos a modificar una única línea de la versión previa.
Vamos a cambiar exactamente este fragmento, los parámetros de la función, de:
first_name, last_name
a:
first_name: str, last_name: str
Eso es todo.
Esos son los "type hints":
def get_full_name(first_name: str, last_name: str):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
No es lo mismo a declarar valores por defecto, como sería con:
first_name="john", last_name="doe"
Es algo diferente.
Estamos usando los dos puntos (:
), no un símbolo de igual (=
).
Añadir los type hints normalmente no cambia lo que sucedería si ellos no estuviesen presentes.
Pero ahora imagina que nuevamente estás creando la función, pero con los type hints.
En el mismo punto intentas iniciar el auto-completado con Ctrl+Space
y ves:
Con esto puedes moverte hacia abajo viendo las opciones hasta que encuentras una que te suene:
Más motivación¶
Mira esta función que ya tiene type hints:
def get_name_with_age(name: str, age: int):
name_with_age = name + " is this old: " + age
return name_with_age
Como el editor conoce el tipo de las variables no solo obtienes auto-completado, si no que también obtienes chequeo de errores:
Ahora que sabes que tienes que arreglarlo convierte age
a un string con str(age)
:
def get_name_with_age(name: str, age: int):
name_with_age = name + " is this old: " + str(age)
return name_with_age
Declarando tipos¶
Acabas de ver el lugar principal para declarar los type hints. Como parámetros de las funciones.
Este es también el lugar principal en que los usarías con FastAPI.
Tipos simples¶
Puedes declarar todos los tipos estándar de Python, no solamente str
.
Por ejemplo, puedes usar:
int
float
bool
bytes
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
return item_a, item_b, item_c, item_d, item_d, item_e
Tipos con sub-tipos¶
Existen algunas estructuras de datos que pueden contener otros valores, como dict
, list
, set
y tuple
. Los valores internos pueden tener su propio tipo también.
Para declarar esos tipos y sub-tipos puedes usar el módulo estándar de Python typing
.
Él existe específicamente para dar soporte a este tipo de type hints.
Listas¶
Por ejemplo, vamos a definir una variable para que sea una list
compuesta de str
.
De typing
, importa List
(con una L
mayúscula):
from typing import List
def process_items(items: List[str]):
for item in items:
print(item)
🤓 Other versions and variants
def process_items(items: list[str]):
for item in items:
print(item)
Declara la variable con la misma sintaxis de los dos puntos (:
).
Pon List
como el tipo.
Como la lista es un tipo que permite tener un "sub-tipo" pones el sub-tipo en corchetes []
:
from typing import List
def process_items(items: List[str]):
for item in items:
print(item)
🤓 Other versions and variants
def process_items(items: list[str]):
for item in items:
print(item)
Esto significa: la variable items
es una list
y cada uno de los ítems en esta lista es un str
.
Con esta declaración tu editor puede proveerte soporte inclusive mientras está procesando ítems de la lista.
Sin tipos el auto-completado en este tipo de estructura es casi imposible de lograr:
Observa que la variable item
es unos de los elementos en la lista items
.
El editor aún sabe que es un str
y provee soporte para ello.
Tuples y Sets¶
Harías lo mismo para declarar tuple
s y set
s:
from typing import Set, Tuple
def process_items(items_t: Tuple[int, int, str], items_s: Set[bytes]):
return items_t, items_s
🤓 Other versions and variants
def process_items(items_t: tuple[int, int, str], items_s: set[bytes]):
return items_t, items_s
Esto significa:
- La variable
items_t
es untuple
con 3 ítems, unint
, otroint
, y unstr
. - La variable
items_s
es unset
y cada uno de sus ítems es de tipobytes
.
Diccionarios (Dicts)¶
Para definir un dict
le pasas 2 sub-tipos separados por comas.
El primer sub-tipo es para los keys del dict
.
El segundo sub-tipo es para los valores del dict
:
from typing import Dict
def process_items(prices: Dict[str, float]):
for item_name, item_price in prices.items():
print(item_name)
print(item_price)
🤓 Other versions and variants
def process_items(prices: dict[str, float]):
for item_name, item_price in prices.items():
print(item_name)
print(item_price)
Esto significa:
- La variable
prices
es undict
:- Los keys de este
dict
son de tipostr
(Digamos que son el nombre de cada ítem). - Los valores de este
dict
son de tipofloat
(Digamos que son el precio de cada ítem).
- Los keys de este
Clases como tipos¶
También puedes declarar una clase como el tipo de una variable.
Digamos que tienes una clase Person
con un nombre:
from typing import Optional
def say_hi(name: Optional[str] = None):
if name is not None:
print(f"Hey {name}!")
else:
print("Hello World")
🤓 Other versions and variants
def say_hi(name: str | None = None):
if name is not None:
print(f"Hey {name}!")
else:
print("Hello World")
Entonces puedes declarar una variable que sea de tipo Person
:
from typing import Optional
def say_hi(name: Optional[str] = None):
if name is not None:
print(f"Hey {name}!")
else:
print("Hello World")
🤓 Other versions and variants
def say_hi(name: str | None = None):
if name is not None:
print(f"Hey {name}!")
else:
print("Hello World")
Una vez más tendrás todo el soporte del editor:
Modelos de Pydantic¶
Pydantic es una library de Python para llevar a cabo validación de datos.
Tú declaras la "forma" de los datos mediante clases con atributos.
Cada atributo tiene un tipo.
Luego creas un instance de esa clase con algunos valores y Pydantic validará los valores, los convertirá al tipo apropiado (si ese es el caso) y te dará un objeto con todos los datos.
Y obtienes todo el soporte del editor con el objeto resultante.
Tomado de la documentación oficial de Pydantic:
class Person:
def __init__(self, name: str):
self.name = name
def get_person_name(one_person: Person):
return one_person.name
Información
Para aprender más sobre Pydantic mira su documentación.
FastAPI está todo basado en Pydantic.
Vas a ver mucho más de esto en práctica en el Tutorial - User Guide.
Type hints en FastAPI¶
FastAPI aprovecha estos type hints para hacer varias cosas.
Con FastAPI declaras los parámetros con type hints y obtienes:
- Soporte en el editor.
- Type checks.
...y FastAPI usa las mismas declaraciones para:
- Definir requerimientos: desde request path parameters, query parameters, headers, bodies, dependencies, etc.
- Convertir datos: desde el request al tipo requerido.
- Validar datos: viniendo de cada request:
- Generando errores automáticos devueltos al cliente cuando los datos son inválidos.
- Documentar la API usando OpenAPI:
- que en su caso es usada por las interfaces de usuario de la documentación automática e interactiva.
Puede que todo esto suene abstracto. Pero no te preocupes que todo lo verás en acción en el Tutorial - User Guide.
Lo importante es que usando los tipos de Python estándar en un único lugar (en vez de añadir más clases, decorator, etc.) FastAPI hará mucho del trabajo por ti.
Información
Si ya pasaste por todo el tutorial y volviste a la sección de los tipos, una buena referencia es la "cheat sheet" de mypy
.