*args — recueille un nombre arbitraire d'arguments positionnels dans un tuple.
**kwargs — recueille un nombre arbitraire d'arguments nommés (clé=valeur) dans un dictionnaire.
Cela permet d'écrire des fonctions avec une interface flexible :
def my_func(a, b, *args, **kwargs): print(a, b) print(args) print(kwargs) my_func(1, 2, 3, 4, x=10, y=20) # 1 2 # (3, 4) # {'x': 10, 'y': 20}
Les subtilités :
Peut-on changer l'ordre de *args et des arguments nommés avec une valeur par défaut, par exemple :
def foo(a, *args, x=10, **kwargs): pass
ou faut-il toujours placer les valeurs par défaut après *args ?
Réponse :
Dans Python 3, c'est possible ! Comme ça (cela ne fonctionnait pas dans Python 2) :
def foo(a, *args, x=10): pass
Les paramètres après *args ("arguments uniquement par mot-clé") peuvent avoir des valeurs par défaut ou être obligatoires — ils ne peuvent être définis que par nom.
Histoire
Transmission d'arguments incorrects via *args/**kwargs
Dans les services REST API, l'automatisation du marshaling des données était construite via **kwargs. Un paramètre superflu n'a pas été remarqué — la fonction prenait une valeur inattendue dans kwargs, ce qui entraînait une perte de logique de contrôle et des bugs difficiles à déboguer.
Histoire
Duplication d'arguments nommés
Lors de l'appel de la fonction, le développeur a accidentellement spécifié un paramètre à la fois de manière explicite et via **kwargs, par exemple : my_func(a=1, **{"a": 2}). En conséquence — TypeError et arrêt du service.
Histoire
Oubli d'arguments positionnels ou uniquement nommés
Lors de l'écriture d'un décorateur, une transmission incorrecte des paramètres (non respect de l'ordre et de la structure) conduisait à ce que la fonction originale reçoive un nombre incorrect d'arguments, des accords changeant, et des erreurs d'appel TypeError commençaient à apparaître en production.