Ajout d'une valeur de paramètre par défaut avec un indice de type en Python

236

Si j'ai une fonction comme celle-ci:

def foo(name, opts={}):
  pass

Et je veux ajouter des indices de type aux paramètres, comment faire? La façon dont j'ai supposé me donne une erreur de syntaxe:

def foo(name: str, opts={}: dict) -> str:
  pass

Ce qui suit ne génère pas d'erreur de syntaxe, mais cela ne semble pas être la manière intuitive de gérer ce cas:

def foo(name: str, opts: dict={}) -> str:
  pass

Je ne trouve rien dans la typingdocumentation ou sur une recherche Google.

Edit: je ne savais pas comment les arguments par défaut fonctionnaient en Python, mais pour cette question, je garderai les exemples ci-dessus. En général, il est préférable de procéder comme suit:

def foo(name: str, opts: dict=None) -> str:
  if not opts:
    opts={}
  pass
Josh
la source
4
La dernière fonction est la bonne manière. C'est la même manière que la scalalangue le fait aussi.
Israel Unterman
14
vous avez un type par défaut modifiable - qui entraînera des problèmes
noɥʇʎԀʎzɐɹƆ
voir ma réponse de mise à jour, @josh
noɥʇʎԀʎzɐɹƆ
@ noɥʇʎԀʎzɐɹƆ Pas sauf si vous l'utilisez pour, par exemple la mémorisation. : P
Mateen Ulhaq

Réponses:

281

Votre deuxième voie est correcte.

def foo(opts: dict = {}):
    pass

print(foo.__annotations__)

cela produit

{'opts': <class 'dict'>}

Il est vrai que ce n'est pas répertorié dans PEP 484 , mais les indications de type sont une application des annotations de fonction, qui sont documentées dans PEP 3107. La section de syntaxe indique clairement que les arguments de mots clés fonctionnent avec les annotations de fonction de cette manière.

Je déconseille fortement d'utiliser des arguments de mots clés mutables. Plus d'informations ici .

noɥʇʎԀʎzɐɹƆ
la source
1
Voir legacy.python.org/dev/peps/pep-3107/#syntax . L'indication de type n'est qu'une application des annotations de fonction.
chepner
@chepner true. ne savait pas que PEP 3107 avait quelque chose à propos des arguments de mots clés.
noɥʇʎԀʎzɐɹƆ
2
Wow, je ne connaissais pas les arguments par défaut mutables en Python ... en particulier venant de Javascript / Ruby où les arguments par défaut fonctionnent différemment. Je ne vais pas répéter ce qui a déjà été dit ad nauseum autour de SO à ce sujet, je suis juste content d'avoir découvert cela avant de me mordre. Merci!
josh
6
On m'a toujours conseillé d'utiliser None plutôt qu'un type mutable comme {} ou [] ou un objet par défaut car les mutations vers cet objet sans copie profonde persisteront entre les itérations.
MrMesees
2
définissez suffisamment de fonctions avec des arguments de mots clés mutables et ce n'est qu'une question de temps avant de vous retrouver avec une session de débogage de 4 heures remettant en question vos choix de vie
Joseph Sheedy
20

Si vous utilisez la saisie (introduite dans Python 3.5), vous pouvez utiliser typing.Optional, où Optional[X]est équivalent à Union[X, None]. Il est utilisé pour signaler que la valeur explicite de Noneest autorisée. À partir de la frappe .

def foo(arg: Optional[int] = None) -> None:
    ...
Tomasz Bartkowiak
la source
2
Ne devrait-il pas y avoir d'espace autour de l' =in Optional[int] = Nonecomme c'est la convention pour les arguments de mot-clé sans indication de type?
actual_panda
7

J'ai récemment vu ce one-liner:

def foo(name: str, opts: dict=None) -> str:
    opts = {} if not opts else opts
    pass
Kirkalicious
la source
Bonjour @Kirkalicious, merci pour votre réponse. Pourriez-vous expliquer comment cela fonctionne?
Nathan
1
Le dict vide transmis comme paramètre par défaut est le même dict pour chaque appel. Donc, si la fonction la mute, la prochaine fois par défaut sera la valeur mutée de la dernière fois. La définition de Nonela valeur par défaut, puis la vérification à l'intérieur de la méthode évite ce problème en allouant un nouveau dict à chaque fois que la méthode est invoquée.
Ian Goldby