Les Crash-Devs d'un Passionné

args et kwargs en python : les paramètres nommées et positionnels sur des fonctions

/Catégorie/python

Temps de lecture : 5 minutes

Avant de regarder plus en profondeur les paramètres dynamiques, je vous propose un rappel rapide sur une fonction en python

clipboard
Copier le code
1
2
def add(a, b):
   return a + b

Les paramètres

a
et
b
font partie de ce que l'on appelle la signature de la méthode, et leurs valeurs doivent être placées dans l'ordre précis de déclaration (on ne peut pas les inverser,
a
est en 1ère position et
b
en 2ème position)

De plus, leurs valeurs sont obligatoires car aucune valeur par défaut n'est renseignée.

Si on part de cette fonction tout simple d'addition, on se rend vite compte de ses limites. Elle ne peut pas prendre plusieurs paramètres. Pour faire cela on est obligé de modifier la signature de la fonction

clipboard
Copier le code
1
2
def add(a, b, c):
   return a + b + c

C'est une solution peu dynamique. En effet si notre programme veut passer un nombre indéterminé de paramètres (qui pourrait dépendre d'un calcul provenant d'une autre partie de l'application), ce que nous venons de voir ne le permet pas.

Nous allons donc voir comment faire cela tout en ayant un code python souple

Les paramètres positionnels : *args

On les trouve souvent sous *args dans la littérature même si ce n'est qu'une convention. On peut leur donner le nom que vous préférez (même si je vous conseille fortement de respecter la convention), il faut juste conserver le *

Ce caractère n'est pas un pointeur (les développeurs C connaissent très bien le calvaire de cette allocation mémoire, bon là ce n'est pas le sujet), c'est une déstructuration de liste que l'on nomme unpack

Du code vaut mieux qu'un long discours, alors voici un exemple simple d'un usage d'unpack de liste

clipboard
Copier le code
1
2
3
4
5
>>> a = [1, 2, 3]
>>> print(*a)
1 2 3
>>> print(a)
[1, 2, 3]

Dans cet exemple il prend les valeurs de cette liste pour les passer en paramètre de la fonction print()

D'ailleurs si on regarde la doc de cette fonction on se rend compte qu'elle est compatible avec l'unpacking de liste

clipboard
Copier le code
1
2
3
4
5
>>> help(print)
Help on built-in function print in module builtins:

print(*args, sep=' ', end='\n', file=None, flush=False)
...

Vous trouverez une autre mise en situation du unpack quand on manipule une liste

On va appliquer ce concept sur notre fonction d'addition

clipboard
Copier le code
1
2
3
4
5
6
7
>>> def add(*args):
...     print(args)
...     
>>> add(1, 2)
(1, 2)
>>> add(1, 2, 3)
(1, 2, 3)

On se rend compte que notre fonction s'adapte à ces paramètres sans se crasher (je n'ai pas implémenté la logique d'addition mais vous avez compris le concept)

Les paramètres nommés : **kwargs

On voit également **kwargs dans la littérature même si ce n'est qu'une convention. On peut leur donner le nom que vous préférez (même si je vous conseille fortement de respecter la convention), il faut juste conserver le **

C'est une déstructuration de dictionnaire que l'on nomme unpack, le même concept que nous venons de voir au-dessus pour une liste, mais cette fois appliqué à un dictionnaire.

On garde le même principe que sur les paramètres positionnels mais cette fois sur notre fonction on doit nommer le nom du paramètre

clipboard
Copier le code
1
2
3
4
5
6
7
>>> def add(**kwargs):
...     print(kwargs)
...     
>>> add(a=1, b=2)
{'a': 1, 'b': 2}
>>> add(a=1, b=2, c=3)
{'a': 1, 'b': 2, 'c': 3}

On remarque également que la fonction s'adapte parfaitement à la fluctuation des paramètres, qui cette fois sont de type dictionnaire

Comment mixer à la fois les nommés et positionnels

On peut évidemment combiner les 2 types de paramètres, mais on doit impérativement les classer dans un ordre précis. Les paramètres positionnels et ensuite les paramètres nommés

Encore une fois voici un court exemple pour mieux comprendre

clipboard
Copier le code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> def add(*args, **kwargs):
...     print(args, kwargs)
...     
>>> add(0, a=1, b=2)
(0,) {'a': 1, 'b': 2}
>>> add(a=0, 1)
  File "<python-input-12>", line 1
    add(a=0, 1)
              ^
SyntaxError: positional argument follows keyword argument

C'est une solution très souple lorsqu'on ne connaît pas à l'avance les paramètres de la fonction.

Quand l'utiliser ?

En cas d'absolue nécessité, car cela alourdit le code. On ne peut pas, au 1er coup d'œil, voir ce qu'attend la fonction : on doit aller regarder son contenu et lire attentivement la documentation.

D'ailleurs si vous l'utilisez mettez une docstring avec des exemples

J'utilise très souvent l'unpacking de liste sur des fonctions helpers pour faire de la construction de chaîne de caractères ou indirectement comme cela

clipboard
Copier le code
1
logger.info("My message %s %s", (a, b))

Ou encore du unpacking de dictionnaire pour injecter mes données dans un objet

pydantic

clipboard
Copier le code
1
2
data = {"name": "David", "zip", "63"}
MyPydanticObject(**data)

Pour en savoir plus sur les objets pydantic, vous pouvez consulter mon article sur Annotated

Également si vous êtes amené à construire des décorateurs, c'est un fondamental à connaître pour construire convenablement ce décorateur