Estoy tratando de entender cómo usar la sugerencia de tipo Optional
. Desde PEP-484 , sé que puedo usar Optional
para def test(a: int = None)
ya sea como def test(a: Union[int, None])
o def test(a: Optional[int])
.
Pero, ¿qué hay de seguir ejemplos?
def test(a : dict = None): #print(a) ==> {'a': 1234} #or #print(a) ==> None def test(a : list = None): #print(a) ==> [1,2,3,4, 'a', 'b'] #or #print(a) ==> None
Si Optional[type]
parece significar lo mismo que Union[type, None]
, ¿por qué debería usar Optional[]
?
Optional[...]
es una notación abreviada para Union[..., None]
, que le indica al verificador de tipos que se requiere un objeto del tipo específico o que no se requiere None
. ...
representa cualquier sugerencia de tipo válida , incluidos los tipos compuestos complejos o una Union[]
de más tipos. Siempre que tenga un argumento de palabra clave con el valor predeterminado None
, debe usar Optional
. (Nota: si tiene como objetivo Python 3.10 o más reciente, PEP 604 introdujo una mejor sintaxis, consulte a continuación).
Entonces, para sus dos ejemplos, tiene tipos de contenedores de dict
y list
, pero el valor predeterminado para el argumento de a
palabra clave muestra que None
también está permitido, así que use Optional[...]
:
from typing import Optional def test(a: Optional[dict] = None) -> None: #print(a) ==> {'a': 1234} #or #print(a) ==> None def test(a: Optional[list] = None) -> None: #print(a) ==> [1, 2, 3, 4, 'a', 'b'] #or #print(a) ==> None
Técnicamente, no hay diferencia entre usar Optional[]
en Union[]
o simplemente agregar None
a Union[]
. Así que Optional[Union[str, int]]
y Union[str, int, None]
son exactamente lo mismo.
Personalmente, me quedaría con usar siempre Optional[]
al configurar el tipo para un argumento de palabra clave que usa = None
para establecer un valor predeterminado, esto documenta mejor la razón por la cual None
está permitido. Además, facilita mover la parte Union[...]
a un alias de tipo separado, o eliminar más tarde la parte Optional[...]
si un argumento se vuelve obligatorio.
Por ejemplo, digamos que tienes
from typing import Optional, Union def api_function(optional_argument: Optional[Union[str, int]] = None) -> None: """Frob the fooznar. If optional_argument is given, it must be an id of the fooznar subwidget to filter on. The id should be a string, or for backwards compatibility, an integer is also accepted. """
luego, la documentación se mejora extrayendo Union[str, int]
en un alias de tipo:
from typing import Optional, Union # subwidget ids used to be integers, now they are strings. Support both. SubWidgetId = Union[str, int] def api_function(optional_argument: Optional[SubWidgetId] = None) -> None: """Frob the fooznar. If optional_argument is given, it must be an id of the fooznar subwidget to filter on. The id should be a string, or for backwards compatibility, an integer is also accepted. """
El refactor para mover Union[]
a un alias se hizo mucho más fácil porque se usó Optional[...]
en lugar de Union[str, int, None]
. Después de todo, el valor None
no es un 'ID de subwidget', no es parte del valor, None
está destinado a marcar la ausencia de un valor.
Nota al margen: a menos que su código solo tenga que ser compatible con Python 3.9 o posterior, debe evitar el uso de tipos de contenedores de biblioteca estándar en la sugerencia de tipo, ya que no puede decir nada sobre qué tipos deben contener. Entonces, en lugar de dict
y list
, use typing.Dict
y typing.List
, respectivamente. Y cuando solo lee de un tipo de contenedor, también puede aceptar cualquier tipo de contenedor abstracto inmutable; las listas y las tuplas son objetos de Sequence
, mientras que dict
es un tipo de Mapping
:
from typing import Mapping, Optional, Sequence, Union def test(a: Optional[Mapping[str, int]] = None) -> None: """accepts an optional map with string keys and integer values""" # print(a) ==> {'a': 1234} # or # print(a) ==> None def test(a: Optional[Sequence[Union[int, str]]] = None) -> None: """accepts an optional sequence of integers and strings # print(a) ==> [1, 2, 3, 4, 'a', 'b'] # or # print(a) ==> None
En Python 3.9 y versiones posteriores, todos los tipos de contenedores estándar se han actualizado para admitir su uso en sugerencias de tipo, consulte PEP 585 . Pero , aunque ahora puede usar dict[str, int]
o list[Union[int, str]]
, es posible que desee usar las anotaciones Mapping
y Sequence
más expresivas para indicar que una función no mutará los contenidos ( se tratan como 'solo lectura'), y que las funciones funcionarían con cualquier objeto que funcione como mapeo o secuencia, respectivamente.
Python 3.10 presenta el |
operador de unión en sugerencias de tipo, consulte PEP 604 . En lugar de Union[str, int]
, puede escribir str | int
. En línea con otros lenguajes de tipo sugerido, la forma preferida (y más concisa) de indicar un argumento opcional en Python 3.10 y versiones posteriores, ahora es Type | None
, por ejemplo, str | None
o list | None
Directamente desde los documentos del módulo de tipeo de mypy .
Si bien la respuesta aceptada es la respuesta correcta, una cosa adicional a tener en cuenta es que, en el contexto de kwargs
, tanto Optional[...]
como Union[..., None]
son redundantes e innecesarios. Si está configurando inmediatamente su kwarg en None
, tanto mypy
como los IDE asumen lo obvio y automáticamente tratan el arg como Optional[...]
.
IDE:
mia:
Sin embargo, para variables y valores de retorno de métodos/funciones, Optional[...]
sigue siendo necesario, ya que mypy
no puede saber, en esos casos, asumir nada automáticamente.