Packing et Unpacking d’arguments de fonctions en Python
- 2021-07-27
- Publié par : Christophe DELEUZE
- Catégorie : Python
L’opérateur splat * et double splat ** ne sont pas uniquement utilisés en Python en tant qu’opérateurs mathématiques. Ils peuvent aussi servir dans la manipulation d’arguments de fonctions au travers de deux fonctionnalités de Python qu’il est indispensable de connaitre : le Packing et l’Unpacking.
L’utilisation de l’opérateur * (pour les tuples) et de l’opérateur ** (pour les dictionnaires) fait référence à la notion d’emballage (Packing) / déballage (Unpacking). C’est cette notion associée aux arguments de fonctions que nous allons étudier dans cet article.
La notion d'Unpacking
Considérons une situation avec laquelle nous avons une fonction qui reçoit quatre arguments. Nous voulons faire un appel à cette fonction et nous avons une liste de taille 4 avec nous qui a tous les arguments pour la fonction. Si nous passons simplement une liste à la fonction, l’appel ne fonctionne pas.
def fonction(a, b, c, d):
print(a, b, c, d)
liste = [1, 2, 3, 4]
fonction(liste)
TypeError: fonction() takes exactly 4 arguments (1 given)
Toutefois, nous pouvons utiliser * pour décompresser/déballer (unpack) la liste afin que tous ses éléments puissent être passés en tant que paramètres différents.
def fonction(a, b, c, d):
print(a, b, c, d)
liste = [1, 2, 3, 4]
fonction(*liste)
(1, 2, 3, 4)
Attention, le nombre d’arguments de la fonction doit être le même que le nombre d’éléments contenus dans la liste qui sera décompressée pour les arguments. Voici l’erreur retournée s’il y a une différence :
def fonction(a, b, c):
print(a, b, c)
liste = [1, 2, 3, 4]
fonction(*liste)
Traceback (most recent call last):
File "", line 1, in
TypeError: fonction() takes 3 positional arguments but 4 were given
Toujours pour illustrer notre propos, voici un exemple utilisant l’unpacking avec la fonction range() qui attend au moins deux arguments.
>>> range(3, 6) # cas normal
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args) # cas avec unpack
[3, 4, 5]
La notion de Packing
Lorsque nous ne savons pas combien d’arguments doivent être transmis à une fonction, nous pouvons utiliser le Packing pour emballer / compresser tous les arguments dans un tuple.
def somme(*args):
return sum(args)
somme(1, 2, 3, 4, 5)
somme(10, 20)
15
30
La fonction ci-dessus somme() effectue un packing pour regrouper tous les arguments que cet appel de méthode reçoit dans une seule variable. Une fois que nous avons cette variable « packée », nous pouvons faire ce que nous voulons avec de la même façon que nous le ferions avec un tuple normal. args[0] et args[1] vous donneraient respectivement le premier et le deuxième argument. Étant donné que nos tuples sont immuables, vous pouvez convertir le tuple args en une liste afin que vous puissiez également modifier, supprimer et réorganiser les éléments.
Exemple de Packing et d'Unpacking utilisés conjointement
Pour illustrer les deux notions précédentes que nous avons abordées, voici un exemple qui montre à la fois l’emballage et le déballage.
# Fonction qui PACK les arguments
# Cette fonction accepte n'importe quel nombre d'arguments et créé un tuple affectué à la variable args
def fonction(*args):
# Modification du premier arguments et du second s'il existe
if len(args)>1:
# Converti le tuple args en une liste
args = list(args)
# modification du premier et du second argument
args[0] = "mon nouveau premier argument"
args[1] = "mon nouveau second argument"
# UNPACKING des arguments du tuple affecté à la variable args pour la fonction print à des fins d'affichage
print(*args)
fonction('salut', 'ça', 'farte?')
mon nouveau premier argument mon nouveau second argument farte?
Dans cet exemple, notre fonction accepte un nombre inconnu d’arguments à l’aide de *args. Les arguments sont packés dans un tuple nommé args. Puis, après avoir converti le tuple en liste et modifié les deux premiers arguments, j’unpack les éléments de la liste à l’aide de *args pour que print() puisse les afficher de la même façon que si j’avais fait : print(args[0], args[1], args[2]).
Pour résumer :
- l’emploi de
*argsdans la définition d’une fonction signifie que l’on fait du packing ; - l’emploi de
*argscomme argument donné à une fonction qui va être appelée signifie que l’on fait de l’unpacking.
Sur la base de l’exemple précédent, voici l’exemple le plus minimal possible d’un unpacking suivi d’un packing :
# Fonction qui PACK les arguments
# Cette fonction accepte n'importe quel nombre d'arguments et créé un tuple affectué à la variable args
def fonction(*args):
# UNPACKING des arguments du tuple affecté à la variable args pour la fonction print à des fins d'affichage
print(*args)
Encore une fois, il faut bien garder à l’esprit que le tuple attribué à la variable args n’existe que dans la portée de la fonction.
Équivalent du Packing et de l'Unpacking pour les dictionnaires
Le packing et l’unpacking ne sont pas des fonctionnalités exclusives des tuples et listes. L’équivalent de * qui est utilisé avec des tuples et des listes est ** pour les dictionnaires.
Unpacking d'un dictionnaire
Commençons par illustrer l’unpacking.
def fonction(a, b, c):
print(a, b, c)
# Unpack d'un dictionnaire
d = {'a':2, 'b':4, 'c':10}
fonction(**d)
2 4 10
Ici, ** a décompressé le dictionnaire utilisé et transmis les éléments du dictionnaire en tant qu’arguments avec mot-clé (keyword arguments – kwargs) à la fonction. Donc, écrire fonction(1, **d) est équivalent à écrire fonction(1, b=4, c=10).
Packing d'un dictionnaire
Pour ce qui est du packing avec des dictionnaires, rien de compliqué :
def fonction(**kwargs):
# kwargs est un dictionnaire
print(type(kwargs))
# Affichage des clés du dictionnaire
for key in kwargs:
print(f"{key} = {kwargs[key]}")
fonction(prenom="Brice", ville="Nice", profession="Casseur")
<classe 'dict'>
prenom = Brice
profession = Nice
profession = Casseur
Chaque mot clé associé à un argument ainsi que sa valeur a bien été pack dans un dictionnaire affecté à une variable nommée kwargs.
Le mot de la fin
*args est utilisé avec les arguments positionnels et **kargs avec les arguments à mot clés.La combinaison des deux est très souvent utilisée dans de nombreux cas, par exemple en programmation de socket pour envoyer un grand nombre de requêtes à un serveur. Ils sont aussi utilisés dans le framework Django pour envoyer des arguments variables aux fonctions d’affichage. Enfin, vous les verrez partout en association avec des décorateurs avec lesquels ils sont utilisés en tant que fonction wrapper qui permet de rendre le code du décorateur adaptable à toutes les fonctions sans avoir besoin de connaitre les arguments.
Attention ! bien que cela soit très puissant, utiliser n’importe comment cela pourrait rendre votre code illisible, ils doivent donc être manipulés avec précaution.
Besoin d’un complément d’info ? Les commentaires sont là pour ça.