Arguments et kwargs en Python: Démystifiés – Real Python

By | septembre 4, 2019

Cours Python en ligne

Parfois, lorsque vous examinez une définition de fonction en Python, vous pouvez constater qu’elle nécessite deux arguments étranges: * args et ** kwargs. Si vous vous êtes déjà demandé quelles sont ces variables particulières ou pourquoi votre IDE les définit dans principale()alors cet article est pour vous. Vous apprendrez à utiliser args et kwargs en Python pour ajouter plus de flexibilité à vos fonctions.

À la fin de l'article, vous saurez:

  • Quoi * args et ** kwargs réellement méchant
  • Comment utiliser * args et ** kwargs dans les définitions de fonctions
  • Comment utiliser un seul astérisque (*) pour décompresser des iterables
  • Comment utiliser deux astérisques (**) pour décompresser les dictionnaires

Cet article suppose que vous sachiez déjà définir des fonctions Python et travailler avec des listes et des dictionnaires.

Passer plusieurs arguments à une fonction

* args et ** kwargs vous permettent de transmettre plusieurs arguments ou arguments de mots clés à une fonction. Prenons l'exemple suivant. C'est une fonction simple qui prend deux arguments et retourne leur somme:

def mon_sum(une, b):
    revenir une + b

Cette fonction fonctionne bien, mais elle est limitée à deux arguments. Que faire si vous devez additionner un nombre variable d'arguments, où le nombre spécifique d'arguments transmis n'est déterminé qu'au moment de l'exécution? Ne serait-il pas formidable de créer une fonction pouvant résumer tout les nombres entiers qui y sont passés, peu importe combien il y en a?

Utilisation de la variable args Python dans les définitions de fonctions

Il existe plusieurs manières de passer un nombre variable d'arguments à une fonction. La première méthode est souvent la plus intuitive pour les personnes ayant une expérience des collections. Vous passez simplement une liste ou un ensemble de tous les arguments à votre fonction. Donc pour mon_sum (), vous pouvez passer une liste de tous les entiers que vous devez ajouter:

# sum_integers_list.py
def mon_sum(mes_intenseurs):
    résultat = 0
    pour X dans mes_intenseurs:
        résultat + = X
    revenir résultat

list_of_integers = [[[[1, 2, 3]
impression(mon_sum(list_of_integers))

Cette implémentation fonctionne, mais chaque fois que vous appelez cette fonction, vous devez également créer une liste d’arguments à lui transmettre. Cela peut être gênant, surtout si vous ne connaissez pas toutes les valeurs qui devraient figurer dans la liste.

C'est ici que * args Cela peut être très utile, car cela vous permet de passer un nombre variable d’arguments de position. Prenons l'exemple suivant:

# sum_integers_args.py
def mon_sum(*args):
    résultat = 0
    # Itérant sur le tuple d'arguments Python
    pour X dans args:
        résultat + = X
    revenir résultat

impression(mon_sum(1, 2, 3))

Dans cet exemple, vous ne transmettez plus de liste à mon_sum (). Au lieu de cela, vous passez trois arguments de position différents. mon_sum () prend tous les paramètres fournis dans l’entrée et les regroupe dans un seul objet itératif nommé args.

Notez que args c'est juste un nom. Vous n'êtes pas obligé d'utiliser le nom args. Vous pouvez choisir le nom de votre choix, tel que entiers:

# sum_integers_args_2.py
def mon_sum(*entiers):
    résultat = 0
    pour X dans entiers:
        résultat + = X
    revenir résultat

impression(mon_sum(1, 2, 3))

La fonction fonctionne toujours, même si vous transmettez l’objet itérable en tant que entiers au lieu de args. Tout ce qui compte, c’est que vous utilisiez le opérateur de déballage (*).

Sachez que l’objet itérable que vous obtiendrez en utilisant l’opérateur de décompression * n'est pas un liste mais un tuple. UNE tuple est semblable à un liste en ce qu'ils supportent tous les deux le slicing et l'itération. Cependant, les tuples sont très différents sur au moins un aspect: les listes peuvent être modifiées, alors que les tuples ne le sont pas. Pour tester cela, exécutez le code suivant. Ce script essaie de changer une valeur d'une liste:

# change_list.py
ma liste = [[[[1, 2, 3]
ma liste[[[[0] = 9
impression(ma liste)

La valeur située au tout premier index de la liste doit être mise à jour pour 9. Si vous exécutez ce script, vous verrez que la liste est bien modifiée:

$ python change_list.py
[9, 2, 3]

La première valeur n'est plus 0, mais la valeur mise à jour 9. Maintenant, essayez de faire la même chose avec un tuple:

# change_tuple.py
mon_tuple = (1, 2, 3)
mon_tuple[[[[0] = 9
impression(mon_tuple)

Ici, vous voyez les mêmes valeurs, sauf qu’elles sont maintenues ensemble comme un tuple. Si vous essayez d'exécuter ce script, vous verrez que l'interpréteur Python renvoie une erreur:

$ python change_tuple.py
Traceback (dernier appel le plus récent):
        Fichier "change_tuple.py", ligne 3, dans 
                mon_tuple[0] = 9
TypeError: l'objet 'tuple' ne prend pas en charge l'affectation d'élément

En effet, un tuple est un objet immuable et ses valeurs ne peuvent plus être modifiées après l'affectation. Gardez cela à l’esprit lorsque vous travaillez avec des n-uplets et * args.

Utilisation de la variable kwargs Python dans les définitions de fonctions

Ok, maintenant tu as compris quoi * args est pour, mais qu'en est-il ** kwargs? ** kwargs fonctionne comme * args, mais au lieu d’accepter des arguments de position, il accepte le mot clé (ou nommé) arguments. Prenons l'exemple suivant:

# concatenate.py
def enchaîner(**Kwargs):
    résultat = ""
    # Itération sur le dictionnaire kwargs Python
    pour se disputer dans Kwargs.valeurs():
        résultat + = se disputer
    revenir résultat

impression(enchaîner(une="Réal", b="Python", c="Est", ="Génial", e="!"))

Lorsque vous exécutez le script ci-dessus, enchaîner() itérera dans le dictionnaire python kwargs et concaténera toutes les valeurs qu'il trouve:

$ python concatenate.py
RealPythonIsGreat!

Comme args, Kwargs est juste un nom qui peut être changé en ce que vous voulez. Encore une fois, l’important est l’utilisation du opérateur de déballage (**).

Ainsi, l'exemple précédent pourrait être écrit comme ceci:

# concatenate_2.py
def enchaîner(**mots):
    résultat = ""
    pour se disputer dans mots.valeurs():
        résultat + = se disputer
    revenir résultat

impression(enchaîner(une="Réal", b="Python", c="Est", ="Génial", e="!"))

Notez que dans l'exemple ci-dessus, l'objet itérable est un standard dict. Si vous parcourez le dictionnaire et souhaitez renvoyer ses valeurs, comme dans l'exemple présenté, vous devez utiliser .valeurs().

En fait, si vous oubliez d’utiliser cette méthode, vous vous retrouverez à parcourir le clés de votre dictionnaire kwargs Python à la place, comme dans l'exemple suivant:

# concatenate_keys.py
def enchaîner(**Kwargs):
    résultat = ""
    # Itération sur les clés du dictionnaire python kwargs
    pour se disputer dans Kwargs:
        résultat + = se disputer
    revenir résultat

impression(enchaîner(une="Réal", b="Python", c="Est", ="Génial", e="!"))

Maintenant, si vous essayez d’exécuter cet exemple, vous remarquerez le résultat suivant:

$ python concatenate_keys.py
abcde

Comme vous pouvez le voir, si vous ne spécifiez pas .valeurs(), votre fonction itérera sur les clés de votre dictionnaire python kwargs, renvoyant un résultat erroné.

Ordre des arguments dans une fonction

Maintenant que vous avez appris quoi * args et ** kwargs Pour, vous êtes prêt à commencer à écrire des fonctions qui prennent un nombre variable d’arguments d’entrée. Mais que se passe-t-il si vous voulez créer une fonction qui prend un nombre variable des deux positions? et arguments nommés?

Dans ce cas, vous devez garder à l'esprit que ordre compte. Tout comme les arguments non par défaut doivent précéder les arguments par défaut, * args doit venir avant ** kwargs.

Pour récapituler, l'ordre correct pour vos paramètres est le suivant:

  1. Arguments standard
  2. * args arguments
  3. ** kwargs arguments

Par exemple, cette définition de fonction est correcte:

# correct_function_definition.py
def ma_fonction(une, b, *args, **Kwargs):
    passer

le * args la variable est listée de manière appropriée avant ** kwargs. Mais que faire si vous essayez de modifier l'ordre des arguments? Par exemple, considérons la fonction suivante:

# wrong_function_definition.py
def ma_fonction(une, b, **Kwargs, *args):
    passer

À présent, ** kwargs vient avant * args dans la définition de la fonction. Si vous essayez d'exécuter cet exemple, une erreur de l'interpréteur s'affichera:

$ python Incorrect_function_definition.py
  Fichier "Incorrect_function_definition.py", ligne 2
                def my_function (a, b, ** kwargs, * args):
                                                                                                                                                ^
ErreurDeSyntaxe: Syntaxe invalide

Dans ce cas, depuis * args vient après ** kwargs, l'interprète Python lance une Erreur de syntaxe.

Déballer avec les opérateurs Asterisk: * Et **

Vous pouvez maintenant utiliser * args et ** kwargs définir des fonctions Python qui prennent un nombre variable d’arguments d’entrée. Allons un peu plus loin pour comprendre quelque chose de plus sur le déballage des opérateurs.

Les opérateurs de décompression à un et deux astérisques ont été introduits dans Python 2. À partir de la version 3.5, ils sont encore plus puissants grâce à PEP 448. En résumé, les opérateurs de décompression sont des opérateurs qui décompactent les valeurs d'objets itérables en Python. L'opérateur astérisque unique * peut être utilisé sur n’importe quelle itération que Python fournit, tandis que l’opérateur à double astérisque ** ne peut être utilisé que sur les dictionnaires.

Commençons par un exemple:

# print_list.py
ma liste = [[[[1, 2, 3]
impression(ma liste)

Ce code définit une liste puis l’imprime sur la sortie standard:

$ python print_list.py
[1, 2, 3]

Notez comment la liste est imprimée, ainsi que les crochets et les crochets correspondants.

Maintenant, essayez de préparer l'opérateur de décompression * au nom de votre liste:

# print_unpacked_list.py
ma liste = [[[[1, 2, 3]
impression(*ma liste)

Ici le * l'opérateur dit impression() pour déballer la liste en premier.

Dans ce cas, la sortie n'est plus la liste elle-même, mais plutôt le contenu de la liste:

$ python print_unpacked_list.py
1 2 3

Pouvez-vous voir la différence entre cette exécution et celle de print_list.py? Au lieu d'une liste, impression() a pris trois arguments distincts en entrée.

Une autre chose que vous remarquerez est que dans print_unpacked_list.py, vous avez utilisé l'opérateur de déballage * d'appeler une fonction, au lieu d'une définition de fonction. Dans ce cas, impression() prend tous les éléments d'une liste comme s'il s'agissait d'arguments simples.

Vous pouvez également utiliser cette méthode pour appeler vos propres fonctions, mais si votre fonction nécessite un nombre spécifique d'arguments, l'itérable que vous décompressez doit avoir le même nombre d'arguments.

Pour tester ce comportement, considérons ce script:

# unpacking_call.py
def mon_sum(une, b, c):
    impression(une + b + c)

ma liste = [[[[1, 2, 3]
mon_sum(*ma liste)

Ici, mon_sum () déclare explicitement que une, b, et c sont des arguments obligatoires.

Si vous exécutez ce script, vous obtiendrez la somme des trois nombres en ma liste:

$ python unpacking_call.py
6

Les 3 éléments de ma liste correspondre parfaitement avec les arguments requis dans mon_sum ().

Maintenant, regardez le script suivant, où ma liste a 4 arguments au lieu de 3:

# wrong_unpacking_call.py
def mon_sum(une, b, c):
    impression(une + b + c)

ma liste = [[[[1, 2, 3, 4]
mon_sum(*ma liste)

Dans cet exemple, mon_sum () attend encore que trois arguments, mais le * l'opérateur obtient 4 éléments de la liste. Si vous essayez d'exécuter ce script, vous verrez que l'interpréteur Python est incapable de l'exécuter:

$ python wrong_unpacking_call.py
Traceback (dernier appel le plus récent):
        Fichier "wrong_unpacking_call.py", ligne 6, dans 
                mon_sum (* ma_liste)
TypeError: my_sum () prend 3 arguments de position mais 4 ont été donnés

Lorsque vous utilisez le * pour décompresser une liste et passer des arguments à une fonction, c’est exactement comme si vous passiez tous les arguments seuls. Cela signifie que vous pouvez utiliser plusieurs opérateurs de décompression pour obtenir les valeurs de plusieurs listes et les transmettre à une seule fonction.

Pour tester ce comportement, prenons l'exemple suivant:

# sum_integers_args_3.py
def mon_sum(*args):
    résultat = 0
    pour X dans args:
        résultat + = X
    revenir résultat

liste1 = [[[[1, 2, 3]
liste2 = [[[[4, 5]
liste3 = [[[[6, 7, 8, 9]

impression(mon_sum(*liste1, *liste2, *liste3))

Si vous exécutez cet exemple, les trois listes sont décompressées. Chaque élément individuel est passé à mon_sum (), résultant en la sortie suivante:

$ python sum_integers_args_3.py
45

Il existe d’autres utilisations pratiques de l’opérateur de déballage. Par exemple, supposons que vous deviez diviser une liste en trois parties différentes. La sortie doit afficher la première valeur, la dernière valeur et toutes les valeurs intermédiaires. Avec l’opérateur de décompression, vous pouvez le faire en une seule ligne de code:

# extract_list_body.py
ma liste = [[[[1, 2, 3, 4, 5, 6]

une, *b, c = ma liste

impression(une)
impression(b)
impression(c)

Dans cet exemple, ma liste contient 6 éléments. La première variable est assignée à une, le dernier à cet toutes les autres valeurs sont regroupées dans une nouvelle liste b. Si vous exécutez le script, impression() vous montrera que vos trois variables ont les valeurs que vous attendez:

$ python extract_list_body.py
1
[2, 3, 4, 5]
6

Une autre chose intéressante que vous pouvez faire avec l’opérateur de déballage * est de scinder les éléments de tout objet itérable. Cela peut être très utile si vous devez fusionner deux listes, par exemple:

# merging_lists.py
ma_first_list = [[[[1, 2, 3]
ma_second_list = [[[[4, 5, 6]
ma_merged_list = [[[[*ma_first_list, *ma_second_list]

impression(ma_merged_list)

L'opérateur de déballage * est préfixé à la fois ma_first_list et ma_second_list.

Si vous exécutez ce script, vous verrez que le résultat est une liste fusionnée:

$ python merging_lists.py
[1, 2, 3, 4, 5, 6]

Vous pouvez même fusionner deux dictionnaires différents en utilisant l'opérateur de décompression **:

# merging_dicts.py
mon_first_dict = "UNE": 1, "B": 2
mon_second_dict = "C": 3, "RÉ": 4
my_merged_dict = **mon_first_dict, **mon_second_dict

impression(my_merged_dict)

Ici, les iterables à fusionner sont mon_first_dict et mon_second_dict.

L'exécution de ce code génère un dictionnaire fusionné:

$ python merging_dicts.py
'A': 1, 'B': 2, 'C': 3, 'D': 4

Rappelez-vous que le * opérateur travaille sur tout objet itérable. Il peut également être utilisé pour décompresser une chaîne:

# string_to_list.py
une = [[[[*"RealPython"]
impression(une)

En Python, les chaînes sont des objets itérables, donc * va décompresser et placer toutes les valeurs individuelles dans une liste une:

$ python string_to_list.py
['R', 'e', 'a', 'l', 'P', 'y', 't', 'h', 'o', 'n']

L’exemple précédent semble excellent, mais lorsque vous travaillez avec ces opérateurs, il est important de garder à l’esprit la septième règle de Le zen de python par Tim Peters: La lisibilité compte.

Pour voir pourquoi, considérons l'exemple suivant:

# mysterious_statement.py
*une, = "RealPython"
impression(une)

L’opérateur de déballage *, suivi d'une variable, d'une virgule et d'une affectation. C’est beaucoup dans une seule ligne! En fait, ce code n'est pas différent de l'exemple précédent. Ça prend juste la ficelle RealPython et affecte tous les articles à la nouvelle liste une, grâce à l'opérateur de déballage *.

La virgule après le une fait le tour. Lorsque vous utilisez l'opérateur de décompression avec une affectation de variable, Python exige que votre variable résultante soit une liste ou un tuple. Avec la virgule de fin, vous avez défini un tuple avec une seule variable nommée. une.

Bien que ce soit une bonne astuce, de nombreux pythonistes ne considéreraient pas ce code comme très lisible. En tant que tel, il est préférable d’utiliser ce type de construction avec parcimonie.

Conclusion

Vous pouvez maintenant utiliser * args et ** kwargs accepter un nombre variable d'arguments dans vos fonctions. Vous avez également appris quelque chose de plus sur les opérateurs de déballage.

Vous avez appris:

  • Quoi * args et ** kwargs réellement méchant
  • Comment utiliser * args et ** kwargs dans les définitions de fonctions
  • Comment utiliser un seul astérisque (*) pour décompresser des iterables
  • Comment utiliser deux astérisques (**) pour décompresser les dictionnaires

Si vous avez encore des questions, n'hésitez pas à vous adresser à la section commentaires ci-dessous! Pour en savoir plus sur l’utilisation des astérisques en Python, consultez l’article de Trey Hunner sur le sujet.