diff --git a/docs/es/docs/python-types.md b/docs/es/docs/python-types.md index e9fd61629..43a2ea4d1 100644 --- a/docs/es/docs/python-types.md +++ b/docs/es/docs/python-types.md @@ -1,12 +1,12 @@ # Introducción a los Tipos de Python -**Python 3.6+** tiene soporte para "type hints" opcionales. +Python tiene soporte para "type hints" opcionales (también se conocido como "type annotations"). -Estos **type hints** son una nueva sintáxis, desde Python 3.6+, que permite declarar el tipo de una variable. +Estos **type hints** o anotaciones son una sintáxis especial 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. +Usando las declaraciones de tipos para tus variables, los editores y 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. +Este es solo un **tutorial corto** sobre los Python type hints. Solo cubre lo mínimo necesario para usarlos con **FastAPI**... que actualmente es muy poco. Todo **FastAPI** está basado en estos type hints, lo que le da muchas ventajas y beneficios. @@ -49,7 +49,7 @@ En algún punto habrías comenzado con la definición de la función, tenías lo 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`? +Cómo era `upper`? O era `uppercase`? `first_uppercase`? `capitalize`? Luego lo intentas con el viejo amigo de los programadores, el autocompletado del editor. @@ -144,57 +144,101 @@ Por ejemplo, puedes usar: {!../../../docs_src/python_types/tutorial005.py!} ``` -### Tipos con sub-tipos +### Tipos genéricos con argumentos de tipo 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`. +Estos tipos que tienen tipos internos se denominan tipos "**genéricos**". Es posible declararlos, incluso con sus tipos internos. -Él existe específicamente para dar soporte a este tipo de type hints. +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. + +#### Nuevas versiones de Python + +La sintaxis que usa `typing` es **compatible** con todas las versiones, desde Python 3.6 hasta las más recientes, incluidas Python 3.9, Python 3.10, etc. + +A medida que avanza Python, las **versiones más nuevas** vienen con soporte mejorado para estas anotaciones de tipo y en muchos casos ni siquiera necesitarás importar y usar el módulo `typing` para declarar las anotaciones de tipo. + +Si puedes elegir una versión más reciente de Python para tu proyecto, podrás aprovechar esa simplicidad adicional. + +En toda la documentación hay ejemplos compatibles con cada versión de Python (cuando existan diferencias). + +Por ejemplo, "**Python 3.6+**" significa que es compatible con Python 3.6 o superior (incluidos 3.7, 3.8, 3.9, 3.10, etc.). Y "**Python 3.9+**" significa que es compatible con Python 3.9 o superior (incluido 3.10, etc.). + +Si puede utilizar las **últimas versiones de Python**, utilice los ejemplos de la última versión, que tendrán la **mejor y más simple sintaxis**, por ejemplo, "**Python 3.10+**". #### 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): +=== "Python 3.9+" -```Python hl_lines="1" -{!../../../docs_src/python_types/tutorial006.py!} -``` + Declara la variable con la misma sintáxis de los dos puntos (`:`). -Declara la variable con la misma sintáxis de los dos puntos (`:`). + Como tipo, pon `list`. -Pon `List` como el tipo. + Como la lista es un tipo que contiene algunos tipos internos, colócalos entre corchetes `[]`: -Como la lista es un tipo que permite tener un "sub-tipo" pones el sub-tipo en corchetes `[]`: + ```Python hl_lines="1" + {!> ../../../docs_src/python_types/tutorial006_py39.py!} + ``` -```Python hl_lines="4" -{!../../../docs_src/python_types/tutorial006.py!} -``` +=== "Python 3.8+" -Esto significa: la variable `items` es una `list` y cada uno de los ítems en esta lista es un `str`. + De `typing`, importa `List` (con una `L` mayúscula): + + ``` Python hl_lines="1" + {!> ../../../docs_src/python_types/tutorial006.py!} + ``` + + Declara la variable con la misma sintáxis de los dos puntos (`:`). + + Como tipo, pon la `List` importada desde typing`. + + Como la lista es un tipo que contiene algunos tipos internos, colócalos entre corchetes `[]`: + + ```Python hl_lines="4" + {!> ../../../docs_src/python_types/tutorial006.py!} + ``` + +!!! info "Información" + Estos tipos internos entre corchetes se denominan "argumentos de tipo". + + En este caso `str` es un argumentos de tipo pasado a la `List` (or `list` in Python 3.9 and above). + +Esto significa: "la variable `items` es una `list`, y cada uno de los ítems en esa lista es un `str`". + +!!! tip "Consejo" + Si estas utilizando 3.9 o superior, no tienes que importar `List` desde `typing`, puedes utilizar `list` en su defecto. Con esta declaración tu editor puede proveerte soporte inclusive mientras está procesando ítems de la lista. -Sin tipos el autocompletado en este tipo de estructura es casi imposible de lograr: - +Sin tipos el autocompletado 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 +#### Tuplas y Sets Harías lo mismo para declarar `tuple`s y `set`s: -```Python hl_lines="1 4" -{!../../../docs_src/python_types/tutorial007.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="1" + {!> ../../../docs_src/python_types/tutorial007_py39.py!} + ``` + +=== "Python 3.8+" + + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial007.py!} + ``` Esto significa: -* La variable `items_t` es un `tuple` con 3 ítems, un `int`, otro `int`, y un `str`. +* La variable `items_t` es una `tuple` con 3 ítems, un `int`, otro `int`, y un `str`. * La variable `items_s` es un `set` y cada uno de sus ítems es de tipo `bytes`. #### Diccionarios (Dicts) @@ -205,15 +249,164 @@ El primer sub-tipo es para los keys del `dict`. El segundo sub-tipo es para los valores del `dict`: -```Python hl_lines="1 4" -{!../../../docs_src/python_types/tutorial008.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="1" + {!> ../../../docs_src/python_types/tutorial008_py39.py!} + ``` + +=== "Python 3.8+" + + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial008.py!} + ``` Esto significa: * La variable `prices` es un `dict`: - * Los keys de este `dict` son de tipo `str` (Digamos que son el nombre de cada ítem). - * Los valores de este `dict` son de tipo `float` (Digamos que son el precio de cada ítem). + * Los keys de este `dict` son de tipo `str` (digamos que son el nombre de cada ítem). + * Los valores de este `dict` son de tipo `float` (digamos que son el precio de cada ítem). + +#### Union + +Puedes declarar que una variable puede ser cualquiera de **varios tipos**, por ejemplo, una `int` o una `str`. + +En Python 3.6 y superiores (incluido Python 3.10) puede usar el tipo `Union` de `typing` y poner entre corchetes los tipos posibles a aceptar. + +En Python 3.10 también hay una **nueva sintaxis** donde puedes poner los tipos posibles separados por una barra vertical (`|` ). + +=== "Python 3.10+" + + ```Python hl_lines="1" + {!> ../../../docs_src/python_types/tutorial008b_py310.py!} + ``` + +=== "Python 3.8+" + + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial008b.py!} + ``` + +En ambos casos, esto significa que `item` podría ser un `int` o un `str`. + +#### Posibilidad `None` + +Puedes declarar que un valor podría tener un tipo, como `str`, pero que también podría ser `None`. + +En Python 3.6 y superior (incluido Python 3.10), puede declararlo importando y usando `Optional` desde el módulo "escribir". + +```Python hl_lines="1 4" +{!../../../docs_src/python_types/tutorial009.py!} +``` + +Usar `Optional[str]` en lugar de solo `str` permitirá que el editor te ayude a detectar errores en los que podrías asumir que un valor es siempre una `str`, cuando en realidad también podría ser `None`. + +`Optional[Something]` es en realidad un atajo para `Union[Something, None]`, son equivalentes. + +Esto también significa que en Python 3.10, puedes usar `Something | None`: + +=== "Python 3.10+" + + ```Python hl_lines="1" + {!> ../../../docs_src/python_types/tutorial009_py310.py!} + ``` + +=== "Python 3.8+" + + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial009.py!} + ``` + +=== "Python 3.8+ alternative" + + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial009b.py!} + ``` + +#### Usar `Union` o `Optional` + +Si está utilizando una versión de Python inferior a 3.10, aquí tiene un consejo desde mi punto de vista muy **subjetivo**: + +* 🚨 Evite el uso de `Optional[SomeType]` +* En su lugar ✨ **use `Union[SomeType, None]`** ✨. + +Ambos son equivalentes y en el fondo son iguales, pero recomendaría `Union` en lugar de `Optional` porque la palabra "**optional**" parecería implicar que el valor es opcional, y en realidad significa "puede ser `None`”, incluso si no es opcional y sigue siendo obligatorio. + +Creo que "Union[SomeType, None]" es más explícito sobre lo que significa. + +Se trata sólo de las palabras y los nombres. Pero esas palabras pueden afectar la forma en que usted y sus compañeros de equipo piensan sobre el código. + +Como ejemplo, tomemos esta función: + +```Python hl_lines="1 4" +{!../../../docs_src/python_types/tutorial009c.py!} +``` + +El parámetro `nombre` se define como `Optional[str]`, pero **no es opcional**, no se puede llamar a la función sin el parámetro: + +```Python +say_hi() # Oh, no, this throws an error! 😱 +``` + +El parámetro `name` es **aún requerido** (no *opcional*) porque no tiene un valor predeterminado. Aún así, `name` acepta `None` como valor: + +```Python +say_hi(name=None) # This works, None is valid 🎉 +``` + +La buena noticia es que una vez que estés en Python 3.10 no tendrás que preocuparte por eso, ya que podrás simplemente usar `|` para definir uniones de tipos: + +```Python hl_lines="1 4" +{!../../../docs_src/python_types/tutorial009c_py310.py!} +``` + +Y entonces no tendrá que preocuparse por nombres como `Optional` y `Union`. 😎 + +#### Tipos Genéricos + +Estos tipos que toman parámetros de tipo entre corchetes se denominan **Tipos genéricos** o **Genéricos**, por ejemplo: + +=== "Python 3.10+" + + Puede utilizar los mismos tipos integrados que los genéricos (con corchetes y tipos dentro): + + * `list` + * `tuple` + * `set` + * `dict` + + Y lo mismo que con Python 3.8, desde el módulo `typing`: + + * `Union` + * `Optional` (lo mismo que con Python 3.8) + * ...y otros. + + En Python 3.10, como alternativa al uso de los genéricos `Union` y `Optional`, puedes usar la barra vertical (`| `) para declarar uniones de tipos, eso es mucho mejor y más simple. + +=== "Python 3.9+" + + Puede utilizar los mismos tipos integrados que los genéricos (con corchetes y tipos dentro): + + * `list` + * `tuple` + * `set` + * `dict` + + Y lo mismo que con Python 3.8, desde el módulo `typing`: + + * `Union` + * `Optional` + * ...y otros. + +=== "Python 3.8+" + + * `List` + * `Tuple` + * `Set` + * `Dict` + * `Union` + * `Optional` + * ...y otros. ### Clases como tipos @@ -222,19 +415,23 @@ También puedes declarar una clase como el tipo de una variable. Digamos que tienes una clase `Person`con un nombre: ```Python hl_lines="1-3" -{!../../../docs_src/python_types/tutorial009.py!} +{!../../../docs_src/python_types/tutorial010.py!} ``` Entonces puedes declarar una variable que sea de tipo `Person`: ```Python hl_lines="6" -{!../../../docs_src/python_types/tutorial009.py!} +{!../../../docs_src/python_types/tutorial010.py!} ``` Una vez más tendrás todo el soporte del editor: +Observe que esto significa que "`one_person` es una **instance** de la clase `Person`". + +No significa que "`one_person` sea la **clase** llamada `Person`". + ## Modelos de Pydantic Pydantic es una library de Python para llevar a cabo validación de datos. @@ -243,44 +440,98 @@ 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. +Luego creas una 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: +Un ejemplo de la documentación oficial de Pydantic: -```Python -{!../../../docs_src/python_types/tutorial010.py!} -``` +=== "Python 3.10+" + + ```Python + {!> ../../../docs_src/python_types/tutorial011_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python + {!> ../../../docs_src/python_types/tutorial011_py39.py!} + ``` + +=== "Python 3.8+" + + ```Python + {!> ../../../docs_src/python_types/tutorial011.py!} + ``` !!! info "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](tutorial/index.md){.internal-link target=_blank}. +Vas a ver mucho más de esto en práctica en el [Tutorial - Guía de Usuario](tutorial/index.md){.internal-link target=_blank}. -## Type hints en **FastAPI** +!!! tip "Consejo" + Pydantic tiene un comportamiento especial cuando usas `Optional` o `Union[Something, None]` sin un valor predeterminado, puedes leer más sobre esto en la documentación de Pydantic acerca de Campos opcionales obligatorios. -**FastAPI** aprovecha estos type hints para hacer varias cosas. +## "Type Hints" con Anotaciones de Metadatos -Con **FastAPI** declaras los parámetros con type hints y obtienes: +Python también tiene una característica que permite poner **metadatos adicionales** en estas sugerencias de tipo usando "Annotated". + +=== "Python 3.9+" + + En Python 3.9, `Annotated` es parte de la biblioteca estándar, por lo que puedes importarlo desde `typing`. + + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial013_py39.py!} + ``` + +=== "Python 3.8+" + + En versiones anteriores a Python 3.9, importa `Annotated` desde `typing_extensions`. + + Ya estará instalado con **FastAPI**. + + ```Python hl_lines="1 4" + {!> ../../../docs_src/python_types/tutorial013.py!} + ``` + +Python en sí no hace nada con este `Annotated`. Y para editores y otras herramientas, el tipo sigue siendo "str". + +Pero puedes usar este espacio en `Annotated` para proporcionar a **FastAPI** metadatos adicionales sobre cómo quieres que se comporte tu aplicación. + +Lo importante que debe recordar es que **el primer *argumento de tipo*** que pasa a `Annotated` es el **tipo real**. El resto son solo metadatos para otras herramientas. + +Por ahora, sólo necesitas saber que `Annotated` existe y que es tandard Python. 😎 + +Más adelante verás lo **poderoso** que puede ser. + +!!! tip "Consejo" + El hecho de que sea **standard Python** significa que seguirás obteniendo la **mejor experiencia de desarrollador posible** en tu editor, con las herramientas que utilizas para analizar y refactorizar tu código, etc. ✨ + + Y también que su código será muy compatible con muchas otras herramientas y bibliotecas de Python. 🚀 + +## "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**. +* **Chequeos de tipo**. ...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: +* **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](tutorial/index.md){.internal-link target=_blank}. +Puede que todo esto suene abstracto. Pero no te preocupes que todo lo verás en acción en el [Tutorial - Guía de Usuario](tutorial/index.md){.internal-link target=_blank}. -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. +Lo importante es que usando los tipos estándar de Python en un único lugar (en vez de añadir más clases, decorators, etc.) **FastAPI** hará mucho del trabajo por ti. !!! info "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`.