Comment utiliser les opérateurs de déballage (*, **) en Python ?



Python se distingue comme le langage de programmation le plus répandu. Aujourd’hui, nous allons explorer une de ses fonctionnalités essentielles, souvent méconnue : le déballage.

Vous avez probablement croisé les symboles * et ** dans divers codes, ou même les avez utilisés sans saisir pleinement leur signification. Nous allons décortiquer le concept de déballage et comment l’exploiter pour écrire du code Python plus élégant et efficace.

Voici une liste de notions qui vous seront utiles pour suivre ce tutoriel :

  • Itérable : toute séquence que l’on peut parcourir avec une boucle `for`, comme les ensembles, les listes, les tuples et les dictionnaires.
  • Callable : un objet Python qui peut être appelé à l’aide de parenthèses (), par exemple, `ma_fonction()`.
  • Shell : environnement d’exécution interactif qui permet d’exécuter du code Python. On y accède en lançant « python » dans un terminal.
  • Variable : un nom symbolique qui stocke un objet et auquel est alloué un espace mémoire.

Commençons par éclaircir une source fréquente de confusion : les astérisques en Python sont aussi des opérateurs arithmétiques. Un seul astérisque `*` sert à la multiplication, tandis que deux astérisques `**` représentent l’exponentiation.

>>> 3*3
9
>>> 3**3
27

Pour vérifier cela, ouvrez un shell Python et tapez :

Remarque : Python 3 doit être installé pour suivre ce tutoriel. Si ce n’est pas le cas, consultez notre guide d’installation.

Comme vous le constatez, l’astérisque est placé après le premier nombre et avant le second, indiquant l’utilisation d’opérateurs arithmétiques.

>>> *range(1, 6),
(1, 2, 3, 4, 5)
>>> {**{'vanille':3, 'chocolat':2}, 'fraise':2}
{'vanille': 3, 'chocolat': 2, 'fraise': 2}

En revanche, nous utilisons les astérisques (*, **) avant un itérable pour le déballer, comme dans l’exemple ci-dessus.

Ne vous inquiétez pas si cela n’est pas clair pour le moment. Il s’agissait d’une introduction au déballage en Python. Poursuivez la lecture pour une compréhension complète !

Qu’est-ce que le déballage ?

Le déballage consiste à extraire les éléments d’un itérable, comme les listes, les tuples et les dictionnaires. Imaginez que vous ouvrez une boîte et en sortez divers objets, comme des câbles, des écouteurs ou une clé USB.

Le déballage en Python est une opération similaire au déballage d’une boîte dans la vie courante.

>>> ma_boite = ['câbles', 'écouteurs', 'USB']
>>> element1, element2, element3 = ma_boite

Traduisons cet exemple en code pour une meilleure compréhension :

Comme vous le voyez, nous affectons les trois éléments de la liste `ma_boite` à trois variables : `element1`, `element2` et `element3`. Ce type d’affectation de variables est le principe fondamental du déballage en Python.

>>> element1
'câbles'
>>> element2
'écouteurs'
>>> element3
'USB'

Si vous tentez d’accéder à la valeur de chaque élément, vous constaterez que `element1` fait référence à « câbles », `element2` à « écouteurs », et ainsi de suite.

>>> nouvelle_boite = ['câbles', 'écouteurs', 'USB', 'souris']
>>> element1, element2, element3 = nouvelle_boite
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 3)

Jusqu’à présent, le code semble fonctionner correctement. Mais que se passe-t-il si nous essayons de déballer une liste contenant plus d’éléments que le nombre de variables définies ?

C’est probablement l’erreur à laquelle vous vous attendiez. En clair, nous tentons d’affecter quatre éléments d’une liste à trois variables. Comment Python pourrait-il attribuer correctement les valeurs ? En réalité, cela génère une erreur de type `ValueError`

avec le message « trop de valeurs à déballer (attendu 3) ». Cette erreur survient car nous avons défini trois variables à gauche et quatre valeurs (provenant de la liste `nouvelle_boite`) à droite.

>>> derniere_boite = ['câbles', 'écouteurs']
>>> element1, element2, element3 = derniere_boite
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 2)

Si vous faites l’inverse, c’est-à-dire, plus de variables que de valeurs à déballer, vous obtiendrez également une erreur `ValueError`, mais avec un message légèrement différent :

Remarque : Nous avons utilisé des listes comme exemple, mais cette forme de déballage est applicable à n’importe quel itérable (listes, ensembles, tuples, dictionnaires).

Alors, comment surmonter cette situation ? Existe-t-il une manière de déballer tous les éléments d’un itérable dans un nombre restreint de variables sans déclencher d’erreurs ?

Bien sûr, il existe une solution. Elle fait appel à l’opérateur de déballage ou astérisque (*, **). Voyons comment l’utiliser en Python.

Comment déballer des listes avec l’opérateur *

L’opérateur astérisque

>>> premier, *non_utilise, dernier = [1, 2, 3, 5, 7]
>>> premier
1
>>> dernier
7
>>> non_utilise
[2, 3, 5]

est utilisé pour extraire toutes les valeurs d’un itérable qui n’ont pas encore été assignées.

>>> premier, *_, dernier = [1, 2, 3, 5, 7]
>>> _
[2, 3, 5]

Imaginons que vous souhaitiez obtenir le premier et le dernier élément d’une liste sans utiliser d’indices. L’opérateur astérisque rend cela possible :

>>> premier, *_, dernier = [1, 2]
>>> premier
1
>>> dernier
2
>>> _
[]

Comme vous pouvez le constater, nous obtenons toutes les valeurs non assignées grâce à l’opérateur astérisque. La méthode la plus courante pour ignorer les valeurs est d’utiliser une variable de soulignement (_), souvent employée comme « variable factice ».

Cette technique est utilisable même si la liste ne contient que deux éléments :

Dans ce cas, la variable de soulignement (variable factice) stocke une liste vide afin que les deux autres variables puissent accéder aux valeurs disponibles dans la liste.

>>> *chaine = 'PythonEstLeMeilleur'

Problème courant

>>> *chaine = 'PythonEstLeMeilleur'
  File "<stdin>", line 1
SyntaxError: starred assignment target must be in a list or tuple

Nous pourrions être tentés de déballer un élément unique d’un itérable de la manière suivante : cependant, le code ci-dessus déclenche une erreur de syntaxe. Ceci parce que selon la

Spécification PEP

>>> *chaine, = 'PythonEstLeMeilleur'
>>> chaine
['P', 'y', 't', 'h', 'o', 'n', 'E', 's', 't', 'L', 'e', 'M', 'e', 'i', 'l', 'l', 'e', 'u', 'r']

Un tuple (ou une liste) est requis à gauche d’une affectation simple

>>> *nombres, = range(5)
>>> nombres
[0, 1, 2, 3, 4]

Si notre objectif est de déballer toutes les valeurs d’un itérable dans une seule variable, il est nécessaire d’utiliser un tuple. Il suffit d’ajouter une simple virgule.

Prenons un autre exemple avec la fonction `range`, qui renvoie une séquence de nombres.

Maintenant que vous maîtrisez le déballage de listes et de tuples avec un astérisque, passons au déballage des dictionnaires.

Comment déballer les dictionnaires avec l’opérateur **

>>> **salutations, = {'bonjour': 'BONJOUR', 'aurevoir':'AU REVOIR'} 
...
SyntaxError: invalid syntax

Alors qu’un seul astérisque est utilisé pour les listes et les tuples, le double astérisque (**) sert à déballer les dictionnaires.

>>> nourriture = {'poisson':3, 'viande':5, 'pâtes':9} 
>>> couleurs = {'rouge': 'intensité', 'jaune':'joie'}
>>> dictionnaire_fusionne = {**nourriture, **couleurs}
>>> dictionnaire_fusionne
{'poisson': 3, 'viande': 5, 'pâtes': 9, 'rouge': 'intensité', 'jaune': 'joie'}

Malheureusement, il n’est pas possible de déballer un dictionnaire dans une seule variable comme nous l’avons fait avec les tuples et les listes. Autrement dit, le code suivant générera une erreur :

Toutefois, nous pouvons utiliser l’opérateur `**` à l’intérieur de fonctions et avec d’autres dictionnaires. Par exemple, si nous voulons créer un dictionnaire fusionné à partir d’autres dictionnaires, le code ci-dessous fera l’affaire :

Il s’agit d’un moyen concis de créer des dictionnaires composés, mais ce n’est pas l’utilisation principale du déballage en Python.

Voyons comment utiliser le déballage avec les fonctions

Déballage dans les fonctions : `args` et `kwargs`

Vous avez sûrement déjà vu `args` et `kwargs` implémentés dans des classes ou des fonctions. Explorons leur rôle dans les fonctions.

>>> def produit(n1, n2):
...     return n1 * n2
... 
>>> nombres = [12, 1]
>>> produit(*nombres)
12

Déballage avec l’opérateur * (args)

>>> produit(12, 1)
12

Supposons que nous ayons une fonction qui calcule le produit de deux nombres.

>>> nombres = [12, 1, 3, 4]
>>> produit(*nombres)
...
TypeError: product() takes 2 positional arguments but 4 were given

Comme vous le voyez, nous déballons les nombres de la liste pour les passer à la fonction, ce qui revient à faire :

>>> def produit(*args):
...     resultat = 1
...     for i in args:
...             resultat *= i
...     return resultat
...
>>> produit(*nombres)
144

Jusqu’ici, tout va bien. Mais que faire si nous voulons passer une liste plus longue ? Cela générera inévitablement une erreur car la fonction reçoit plus d’arguments qu’elle n’est capable d’en gérer.

Nous pouvons résoudre ce problème en emballant la liste directement dans la fonction. Cela crée un itérable interne et nous permet de passer un nombre quelconque d’arguments à la fonction.

Ici, nous traitons le paramètre `args` comme un itérable, en parcourant ses éléments et en retournant le produit de tous les nombres. Notez que la valeur initiale de `resultat` doit être 1, car si elle est à zéro, la fonction retournera toujours zéro. Notez que `args` n’est qu’une convention. Vous pouvez utiliser n’importe quel autre nom de paramètre. Nous pourrions également passer des nombres arbitraires à la fonction sans utiliser de liste, de la même manière qu’avec la fonction intégrée

>>> produit(5, 5, 5)
125
>>> print(5, 5, 5)
5 5 5

print

>>> def test_type(*args):
...     print(type(args))
...     print(args)
... 
>>> test_type(1, 2, 4, 'une chaine')
<class 'tuple'>
(1, 2, 4, 'une chaine')

.

Enfin, regardons le type d’objet des arguments d’une fonction.

Comme le montre le code ci-dessus, le type des arguments sera toujours un tuple et son contenu correspondra à tous les arguments non-nommés passés à la fonction.

Déballage avec l’opérateur ** (kwargs)

>>> def creer_personne(nom, **kwargs):
...     resultat = nom + ': '
...     for cle, valeur in kwargs.items():
...             resultat += f'{cle} = {valeur}, '
...     return resultat
... 
>>> creer_personne('Melissa', id=12112, localisation='londres', valeur_nette=12000)
'Melissa: id = 12112, localisation = londres, valeur_nette = 12000, '

Comme nous l’avons vu précédemment, l’opérateur `**` est spécifiquement utilisé pour les dictionnaires. Cela signifie qu’avec cet opérateur, nous pouvons transmettre des paires clé-valeur à la fonction en tant que paramètres.

Créons une fonction `creer_personne`, qui reçoit un argument positionnel « nom » ainsi qu’un nombre indéterminé d’arguments mots-clés.

Comme vous le voyez, l’instruction `**kwargs` convertit tous les arguments mots-clés en un dictionnaire que nous pouvons parcourir à l’intérieur de la fonction.

>>> def test_kwargs(**kwargs):
...     print(type(kwargs))
...     print(kwargs)
... 
>>> test_kwargs(aleatoire=12, parametres=21)
<class 'dict'>
{'aleatoire': 12, 'parametres': 21}

Remarque : `kwargs` n’est qu’une convention. Vous pouvez choisir un autre nom pour ce paramètre.

Nous pouvons vérifier le type de `kwargs` de la même manière que nous l’avons fait avec `args` :

>>> def ma_fonction_finale(*args, **kwargs):
...     print('Type args: ', type(args))
...     print('args: ', args)
...     print('Type kwargs: ', type(kwargs))
...     print('kwargs: ', kwargs)
... 
>>> ma_fonction_finale('Python', 'Le', 'Meilleur', langage="Python", utilisateurs="Beaucoup")
Type args:  <class 'tuple'>
args:  ('Python', 'Le', 'Meilleur')
Type kwargs:  <class 'dict'>
kwargs:  {'langage': 'Python', 'utilisateurs': 'Beaucoup'}

La variable interne `kwargs` se transforme toujours en un dictionnaire, qui stocke les paires clé-valeur transmises à la fonction.

Enfin, utilisons `args` et `kwargs` dans la même fonction :

Conclusion

  • Les opérateurs de déballage sont particulièrement utiles dans les tâches quotidiennes. Vous savez maintenant comment les utiliser à la fois dans des instructions individuelles et dans les paramètres de fonction.
  • Ce tutoriel vous a appris que :
  • `*` est utilisé pour les tuples et les listes, et `**` pour les dictionnaires.
  • Vous pouvez employer les opérateurs de déballage dans les constructeurs de fonctions et de classes.

`args` sert à passer des paramètres non-nommés aux fonctions, tandis que `kwargs` permet de passer des paramètres nommés aux fonctions.