J'essaie de comprendre comment utiliser l'indicateur de type Optional
. Depuis PEP 434, je sais que je peux utiliser Optional
pour def test(a: int = None)
comme def test(a: Union[int, None])
ou def test(a: Optional[int])
.
Mais qu'en est-il des exemples suivants?
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
Optional[type]
semble vouloir dire la même chose que Union[type, None]
, pourquoi devrais-je utiliser Optional[]
du tout?
Optional[...]
est une notation abrégée pour Union[..., None]
, indiquant au vérificateur de type qu’un objet du type spécifique est requis, oNone
est requis. ...
signifie tout indice de type valide, y compris les types de composés complexes ou un Union[]
de plusieurs types. Chaque fois que vous avez un argument de mot clé avec la valeur par défaut None
, vous devez utiliser Optional
.
Donc, pour vos deux exemples, vous avez les types de conteneur dict
et list
, mais la valeur par défaut de l'argument de mot-clé a
indique que None
est également autorisé. utilisation 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
Notez qu'il n'y a techniquement aucune différence entre utiliser Optional[]
sur un Union[]
, ou simplement en ajoutant None
au Union[]
. Alors Optional[Union[str, int]]
et Union[str, int, None]
sont exactement la même chose.
Personnellement, je resterais avec toujours en utilisant Optional[]
lors de la définition du type d’un argument de mot clé utilisant = None
pour définir une valeur par défaut, ceci documente la raison pour laquelle None
est mieux autorisé. De plus, il est plus facile de déplacer le Union[...]
part dans un alias de type séparé, ou pour supprimer ultérieurement le Optional[...]
partie si un argument devient obligatoire.
Par exemple, disons que vous avez
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.
"""
alors la documentation est améliorée en retirant le Union[str, int]
dans un alias de type:
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.
"""
Le refactor pour déplacer le Union[]
dans un alias a été rendu beaucoup plus facile parce que Optional[...]
a été utilisé à la place de Union[str, int, None]
. La valeur None
n'est pas un "identifiant de sous-widget", elle ne fait pas partie de la valeur. None
est destinée à signaler l'absence de valeur.
Remarque latérale: vous voulez éviter d'utiliser les types de conteneur de bibliothèque standard dans les indicateurs de type, car vous ne pouvez rien dire sur les types qu'ils doivent contenir; donc au lieu de dict
et list
, utilisez typing.List
et typing.Dict
. Et lorsque seulement lecture à partir d'un type de conteneur, vous pouvez également accepter n'importe quel type de conteneur abstrait immuable; les listes et les nuplets sont des objets Sequence
, alors que dict
est un type 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