web-dev-qa-db-fra.com

Appel d'une fonction Python avec * args, ** kwargs et arguments optionnels / par défaut

En python, je peux définir une fonction comme suit:

def func(kw1=None,kw2=None,**kwargs):
   ...

Dans ce cas, je peux appeler func comme:

func(kw1=3,kw2=4,who_knows_if_this_will_be_used=7,more_kwargs=Ellipsis)

Je peux également définir une fonction comme:

def func(arg1,arg2,*args):
    ...

qui peut être appelé

func(3,4,additional,arguments,go,here,Ellipsis)

Enfin, je peux combiner les deux formes

def func(arg1,arg2,*args,**kwargs):
    ...

Mais ce qui ne fonctionne pas, c'est:

func(arg1,arg2,*args,kw1=None,kw2=None,**kwargs):  #SYNTAX ERROR (in python 2 only,  apparently this works in python 3)
    ...

Ma pensée originale était que c'était probablement parce qu'une fonction

def func(arg1,arg2,*args,kw1=None):
    ...

peut être appelé comme

func(1,2,3) #kw1 will be assigned 3

Donc, cela introduirait une certaine ambiguïté quant à savoir si 3 devrait être emballé dans args ou kwargs. Cependant, avec python 3, il est possible de spécifier des arguments de mots clés uniquement:

def func(a,b,*,kw=None):  #can be called as func(1,2), func(1,2,kw=3), but NOT func(1,2,3)
   ...

Avec cela, il semble qu'il n'y ait pas d'ambiguïté syntaxique avec:

def func(a,b,*args,*,kw1=None,**kwargs):
    ...

Cependant, cela amène toujours une erreur de syntaxe (testé avec Python3.2). Y a-t-il une raison à cela qui me manque? Et, existe-t-il un moyen d'obtenir le comportement que j'ai décrit ci-dessus (avoir * args avec des arguments par défaut) - Je sais que je peux simuler ce comportement en manipulant le dictionnaire kwargs à l'intérieur de la fonction.

58
mgilson

Vous pouvez faites cela dans Python 3.

def func(a,b,*args,kw1=None,**kwargs):

Le nu * n'est utilisé que lorsque vous souhaitez spécifier des arguments de mot clé uniquement sans accepter un nombre variable d'arguments positionnels avec *args. Vous n'utilisez pas deux *s.

Pour citer la grammaire, en Python 2 , vous avez

parameter_list ::=  (defparameter ",")*
                    (  "*" identifier [, "**" identifier]
                    | "**" identifier
                    | defparameter [","] )

en Python , vous avez

parameter_list ::=  (defparameter ",")*
                    (  "*" [parameter] ("," defparameter)*
                    [, "**" parameter]
                    | "**" parameter
                    | defparameter [","] )

qui comprend une disposition pour des paramètres supplémentaires après le * paramètre.

MISE À JOUR:

Dernière Python 3 ici .

59
agf

Si vous voulez faire un mélange des deux, n'oubliez pas que * args et ** kwargs doivent être les derniers paramètres spécifiés.

def func(arg1,arg2,*args,kw1=None,kw2=None,**kwargs): #Invalid
def func(arg1,arg2,kw1=None,kw2=None,*args,**kwargs): #Valid

Les commentaires semblent être basés sur un mélange de la façon dont une définition de fonction est construite par rapport à la façon dont les arguments fournis sont affectés aux paramètres spécifiés dans la définition.

Il s'agit de la définition de cette fonction qui comporte 6 paramètres. Il est appelé en lui passant des arguments nommés et non nommés dans un appel de fonction.

Pour cet exemple ... Lorsqu'un argument est nommé lors de l'appel de la fonction, il peut être fourni dans le désordre. arg1 et arg2 sont des paramètres obligatoires et s'ils ne sont pas passés à la fonction en tant qu'arguments nommés, ils doivent être attribués dans l'ordre à partir des arguments non nommés fournis. kw1 et kw2 ont des valeurs par défaut fournies dans la définition de la fonction, elles ne sont donc pas obligatoires, mais si elles ne sont pas fournies en tant qu'arguments nommés, elles prendront toutes les valeurs disponibles parmi les autres arguments fournis sans nom. Tous les arguments non nommés restants sont fournis à la fonction dans un tableau appelé args. Tous les arguments nommés qui n'ont pas de nom de paramètre correspondant dans la définition de la fonction sont fournis à l'appel de fonction dans un dictionnaire appelé kwargs.

6
frank