Avant de regarder plus en profondeur les paramètres dynamiques, je vous propose un rappel rapide sur une fonction en python
Copier le code
1 2 def add(a, b): return a + b
Les paramètres
a
b
a
b
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
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
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
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
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
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
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
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
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