web-dev-qa-db-fra.com

Comment utiliser l'indicateur de type facultatif?

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?

15
jacobcan118

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
22
Martijn Pieters