web-dev-qa-db-fra.com

Décompression des arguments: seuls les arguments nommés peuvent suivre l'expression *

Ce qui suit fonctionne magnifiquement en Python:

def f(x,y,z): return [x,y,z]

a=[1,2]

f(3,*a)

Les éléments de a sont décompressés comme si vous l'aviez appelé comme f(3,1,2) et il retourne [3,1,2]. Formidable!

Mais je ne peux pas décompresser les éléments de a dans les premier deux arguments:

f(*a,3)

Au lieu d'appeler cela comme f(1,2,3), j'obtiens "SyntaxError: seuls les arguments nommés peuvent suivre * expression".

Je me demande simplement pourquoi il doit en être ainsi et s'il y a une astuce intelligente que je ne connais pas pour décompresser des tableaux dans des parties arbitraires de listes d'arguments sans avoir recours à des variables temporaires.

43
dreeves

Comme le souligne la réponse de Raymond Hettinger, cette peut changer a changé en Python 3 et voici une proposition connexe , qui a été acceptée. Particulièrement liée à la question actuelle, voici l'une des modifications possibles de cette proposition qui a été discuté:

Autoriser uniquement une expression suivie comme dernier élément de l'exprlist. Cela simplifierait un peu le code de décompression et permettrait à l'expression suivie d'être affectée à un itérateur. Ce comportement a été rejeté car il serait trop surprenant.

Il y a donc des raisons d'implémentation de la restriction avec le déballage des arguments de fonction mais c'est en effet un peu surprenant!

En attendant, voici la solution de contournement que je cherchais, assez évidente rétrospectivement:

f(*(a+[3]))
33
dreeves

Grâce à PEP 448 - Généralisations de déballage supplémentaires ,

f(*a, 3)

est syntaxe désormais acceptée à partir de Python 3.5 . De même, vous pouvez utiliser la double étoile ** pour déballer l'argument mot-clé n'importe où et l'un ou l'autre peut être utilisé plusieurs fois.

12
Antti Haapala

Il ne doit pas être comme ça. C'était juste la règle que Guido jugeait raisonnable.

Dans Python 3, les règles de déballage ont été quelque peu libéralisées:

>>> a, *b, c = range(10)
>>> a
0
>>> b
[1, 2, 3, 4, 5, 6, 7, 8]
>>> c
9

Selon que Guido estime que cela améliorerait le langage, cette libéralisation pourrait également être étendue aux arguments de fonction.

Voir la discussion sur déballage itératif étend pour quelques réflexions sur la raison pour laquelle Python 3 a changé les règles.

11

f attend 3 arguments (x, y, z, dans cet ordre).

Supposons que L = [1,2]. Lorsque vous appelez f(3, *L), ce que python fait en arrière-plan, c'est d'appeler f(3, 1, 2), sans vraiment connaître la longueur de L .

Que se passe-t-il donc si L était à la place [1,2,3]?

Ensuite, lorsque vous appelez f(3, *L), vous finirez par appeler f(3,1,2,3), ce qui sera une erreur car f attend exactement 3 arguments et vous lui en avez donné 4.

Supposons maintenant L=[1,2]1. Look at what happens when you call F`:

>>> f(3,*L) # works fine
>>> f(*L) # will give you an error when f(1,2) is called; insufficient arguments

Maintenant, vous savez implicitement quand vous appelez f(*L, 3) que 3 sera assigné à z, mais python ne le sait pas. Il sait seulement que le last j de nombreux éléments de l'entrée de f seront définis par le contenu de L. Mais comme il ne connaît pas la valeur de len(L) , il ne peut pas faire d'hypothèses sur le fait que f(*L,3) aurait le bon nombre d'arguments.

Ce n'est cependant pas le cas avec f(3,*L). Dans ce cas, python sait que tous les arguments SAUF le premier seront définis par le contenu de L.

Mais si vous avez nommé des arguments f(x=1, y=2, z=3), alors les arguments affectés par nom seront liés en premier. Ce n'est qu'alors que les arguments positionnels sont liés. Vous faites donc f(*L, z=3). Dans ce cas, z est lié à 3 En premier, puis les autres valeurs sont liées.

Maintenant, fait intéressant, si vous avez fait f(*L, y=3), cela vous donnerait une erreur pour essayer d'affecter à y deux fois (une fois avec le mot-clé, une fois de plus avec le positionnel)

J'espère que cela t'aides

4
inspectorG4dget

Agréable. Cela fonctionne également pour les tuples. N'oubliez pas la virgule:

a = (1,2)
f(*(a+(3,)))
2
strawbot

Vous pouvez utiliser f(*a, z=3) si vous utilisez f(*a, 3), il ne sait pas comment décompresser le paramètre car vous avez fourni 2 paramètres et 2 est le second.

1
Jianzhong He