Définition de **kwargs en Python
En Python, **kwargs est un paramètre spécial utilisé dans la définition d'une fonction pour accepter un nombre variable d'arguments nommés (keyword arguments). Le terme "kwargs" est une abréviation de "keyword arguments". Concrètement, lorsque vous écrivez **kwargs dans la signature d'une fonction, Python collecte tous les arguments nommés supplémentaires passés à cette fonction et les stocke dans un dict (dictionnaire).
Ce mécanisme est extrêmement puissant : il vous permet de créer des fonctions flexibles capables de recevoir un nombre indéterminé de paramètres sans avoir à les déclarer explicitement. C'est un concept fondamental que vous retrouverez dans de nombreuses bibliothèques Python et que nous explorons en profondeur dans notre formation complète sur Python.
Le double astérisque ** est l'opérateur clé. Le nom "kwargs" est une simple convention : vous pourriez tout aussi bien écrire **options ou **parametres. C'est le ** qui indique à Python de capturer les arguments nommés.
Comment fonctionne **kwargs ?
Lorsqu'une fonction est appelée avec des arguments nommés qui ne correspondent pas aux paramètres définis explicitement, Python les regroupe dans un dictionnaire. Les noms des arguments deviennent les clés du dictionnaire, et les valeurs passées deviennent les valeurs associées.
Voici un premier exemple simple :
def afficher_infos(**kwargs):
for cle, valeur in kwargs.items():
print(f"{cle} : {valeur}")
afficher_infos(nom="Alice", age=30, ville="Paris")
Ce code affiche :
nom : Alice
age : 30
ville : Paris
Comme vous pouvez le constater, les trois arguments nommés nom, age et ville ont été automatiquement collectés dans le dictionnaire kwargs. Vous pouvez ensuite parcourir ce dictionnaire avec la méthode .items() pour accéder aux paires clé-valeur.
Différence entre *args et **kwargs
Il est essentiel de bien distinguer *args et **kwargs. Ces deux mécanismes sont complémentaires mais fonctionnent différemment :
| Caractéristique | *args | **kwargs |
|---|---|---|
| Type de conteneur | tuple | dict |
| Arguments acceptés | Positionnels (sans nom) | Nommés (avec nom) |
| Syntaxe d'appel | func(1, 2, 3) | func(a=1, b=2) |
| Accès aux valeurs | Par index | Par clé |
Voici un exemple qui combine les deux :
def fonction_complete(*args, **kwargs):
print("Arguments positionnels :", args)
print("Arguments nommés :", kwargs)
fonction_complete(1, 2, 3, nom="Alice", age=30)
Résultat :
Arguments positionnels : (1, 2, 3)
Arguments nommés : {'nom': 'Alice', 'age': 30}
L'ordre des paramètres dans la signature d'une fonction est important : les paramètres classiques viennent en premier, puis *args, puis **kwargs. Inverser cet ordre provoquera une erreur de syntaxe.
Exemples pratiques d'utilisation de **kwargs
Créer une fonction de configuration flexible
L'un des cas d'utilisation les plus courants de **kwargs est la création de fonctions de configuration. Vous pouvez définir des valeurs par défaut tout en permettant à l'utilisateur de les surcharger :
def configurer_application(**kwargs):
config = {
"debug": False,
"port": 8080,
"host": "localhost",
"timeout": 30
}
config.update(kwargs)
return config
# Utilisation avec des paramètres personnalisés
ma_config = configurer_application(debug=True, port=3000)
print(ma_config)
# {'debug': True, 'port': 3000, 'host': 'localhost', 'timeout': 30}
Dans cet exemple, la fonction définit un dictionnaire de configuration par défaut, puis utilise update() pour remplacer les valeurs spécifiées par l'utilisateur. C'est un patron de conception très répandu dans l'écosystème Python.
Transmettre des arguments à une autre fonction
Une autre utilisation très fréquente de **kwargs est la transmission d'arguments à une fonction sous-jacente. C'est un patron que vous retrouverez souvent dans les décorateurs et les fonctions enveloppes :
def journaliser_appel(func):
def wrapper(*args, **kwargs):
print(f"Appel de {func.__name__} avec kwargs: {kwargs}")
resultat = func(*args, **kwargs)
print(f"Résultat : {resultat}")
return resultat
return wrapper
@journaliser_appel
def calculer_prix(prix_base, taxe=0.2, remise=0):
return prix_base * (1 + taxe) - remise
calculer_prix(100, taxe=0.1, remise=5)
Résultat :
Appel de calculer_prix avec kwargs: {'taxe': 0.1, 'remise': 5}
Résultat : 105.0
Ici, le décorateur journaliser_appel utilise **kwargs dans sa fonction wrapper pour intercepter et transmettre tous les arguments nommés sans avoir besoin de les connaître à l'avance.
Construire des classes avec des options variables
Vous pouvez utiliser **kwargs dans la méthode __init__ d'une class pour permettre une initialisation flexible :
class Widget:
def __init__(self, nom, **kwargs):
self.nom = nom
self.largeur = kwargs.get("largeur", 100)
self.hauteur = kwargs.get("hauteur", 50)
self.couleur = kwargs.get("couleur", "blanc")
self.visible = kwargs.get("visible", True)
def __repr__(self):
return (f"Widget(nom='{self.nom}', largeur={self.largeur}, "
f"hauteur={self.hauteur}, couleur='{self.couleur}')")
# Création avec différentes options
bouton = Widget("MonBouton", couleur="bleu", largeur=200)
print(bouton)
# Widget(nom='MonBouton', largeur=200, hauteur=50, couleur='bleu')
La méthode .get() du dictionnaire est particulièrement utile ici, car elle vous permet de définir une valeur par défaut si la clé n'existe pas dans kwargs.
Dépaquetage de dictionnaire avec **
L'opérateur ** ne sert pas uniquement à capturer des arguments. Il peut aussi dépaqueter un dictionnaire pour le passer comme arguments nommés à une fonction :
def creer_profil(nom, age, ville, profession):
return f"{nom}, {age} ans, {ville} - {profession}"
infos = {
"nom": "Alice",
"age": 30,
"ville": "Paris",
"profession": "Développeuse"
}
# Dépaquetage du dictionnaire
profil = creer_profil(**infos)
print(profil)
# Alice, 30 ans, Paris - Développeuse
Cette technique est extrêmement pratique quand vous avez vos données déjà structurées dans un dictionnaire. L'opérateur ** "déballe" chaque paire clé-valeur du dictionnaire en argument nommé correspondant.
Fusionner des dictionnaires avec **
Depuis Python 3.5, vous pouvez utiliser ** pour fusionner plusieurs dictionnaires de manière élégante :
defauts = {"theme": "clair", "langue": "fr", "notifications": True}
preferences_utilisateur = {"theme": "sombre", "taille_police": 14}
# Fusion avec ** (Python 3.5+)
config_finale = {**defauts, **preferences_utilisateur}
print(config_finale)
# {'theme': 'sombre', 'langue': 'fr', 'notifications': True, 'taille_police': 14}
# Alternative avec | (Python 3.9+)
config_finale_v2 = defauts | preferences_utilisateur
print(config_finale_v2)
# {'theme': 'sombre', 'langue': 'fr', 'notifications': True, 'taille_police': 14}
Notez que lorsque des clés sont identiques, c'est le dernier dictionnaire qui l'emporte. Ici, "theme" vaut "sombre" car preferences_utilisateur est dépacqueté en second.
Combiner paramètres classiques, *args et **kwargs
Lorsque vous combinez différents types de paramètres, vous devez respecter un ordre strict dans la signature de votre fonction def :
def fonction_universelle(param_obligatoire, param_defaut="valeur", *args, **kwargs):
print(f"Obligatoire : {param_obligatoire}")
print(f"Avec défaut : {param_defaut}")
print(f"Args supplémentaires : {args}")
print(f"Kwargs supplémentaires : {kwargs}")
fonction_universelle("hello", "world", 1, 2, 3, debug=True, verbose=False)
Résultat :
Obligatoire : hello
Avec défaut : world
Args supplémentaires : (1, 2, 3)
Kwargs supplémentaires : {'debug': True, 'verbose': False}
L'ordre à respecter est toujours le suivant :
- Paramètres positionnels obligatoires
- Paramètres avec valeur par défaut
*args— arguments positionnels variables**kwargs— arguments nommés variables
**kwargs doit toujours être le dernier paramètre de la signature. Placer quoi que ce soit après **kwargs provoquera une SyntaxError.
Bonnes pratiques avec **kwargs
1. Validez les clés reçues
Un inconvénient de **kwargs est qu'il accepte n'importe quel argument nommé, y compris ceux contenant des fautes de frappe. Pensez à valider les clés :
def configurer(**kwargs):
cles_valides = {"debug", "port", "host", "timeout"}
cles_inconnues = set(kwargs.keys()) - cles_valides
if cles_inconnues:
raise TypeError(f"Arguments inconnus : {cles_inconnues}")
return kwargs
# Ceci lèvera une erreur
try:
configurer(debuq=True) # Faute de frappe !
except TypeError as e:
print(e) # Arguments inconnus : {'debuq'}
2. Préférez les paramètres explicites quand c'est possible
N'utilisez pas **kwargs comme raccourci pour éviter de déclarer des paramètres. Si vous connaissez les paramètres attendus, déclarez-les explicitement pour une meilleure lisibilité et un meilleur support de l'autocomplétion :
# ❌ Moins lisible
def creer_utilisateur(**kwargs):
nom = kwargs.get("nom")
email = kwargs.get("email")
age = kwargs.get("age", 0)
# ✅ Plus explicite et lisible
def creer_utilisateur(nom, email, age=0):
pass
3. Documentez les kwargs attendus
Lorsque vous utilisez **kwargs, ajoutez une docstring détaillée pour indiquer les arguments nommés acceptés :
def envoyer_email(destinataire, sujet, **kwargs):
"""Envoie un email.
Args:
destinataire: Adresse email du destinataire.
sujet: Sujet de l'email.
**kwargs: Options supplémentaires :
- cc (str): Adresse en copie.
- bcc (str): Adresse en copie cachée.
- priorite (int): Niveau de priorité (1-5).
- html (bool): Envoyer en HTML (défaut: False).
"""
cc = kwargs.get("cc")
bcc = kwargs.get("bcc")
priorite = kwargs.get("priorite", 3)
html = kwargs.get("html", False)
# ... logique d'envoi
4. Utilisez kwargs pour l'héritage et la délégation
Dans les hiérarchies de classes, **kwargs est idéal pour transmettre des arguments au constructeur parent sans les lister tous :
class Animal:
def __init__(self, nom, age, **kwargs):
self.nom = nom
self.age = age
self.attributs = kwargs
class Chien(Animal):
def __init__(self, race, **kwargs):
super().__init__(**kwargs)
self.race = race
mon_chien = Chien(race="Labrador", nom="Rex", age=5, couleur="doré")
print(mon_chien.nom) # Rex
print(mon_chien.race) # Labrador
print(mon_chien.attributs) # {'couleur': 'doré'}
Cas d'utilisation avancés
Kwargs avec des annotations de type
Depuis Python 3.12, vous pouvez utiliser TypedDict et Unpack pour typer précisément vos **kwargs :
from typing import TypedDict, Unpack
class ConfigOptions(TypedDict, total=False):
debug: bool
port: int
host: str
def demarrer_serveur(**kwargs: Unpack[ConfigOptions]) -> None:
debug = kwargs.get("debug", False)
port = kwargs.get("port", 8080)
host = kwargs.get("host", "localhost")
print(f"Serveur sur {host}:{port} (debug={debug})")
demarrer_serveur(debug=True, port=3000)
Cette approche offre le meilleur des deux mondes : la flexibilité de **kwargs avec la sécurité du typage statique.
Kwargs dans les fonctions lambda
Les fonctions lambda peuvent également utiliser **kwargs, même si cela reste peu courant :
# Lambda avec **kwargs
afficher = lambda **kwargs: print(", ".join(f"{k}={v}" for k, v in kwargs.items()))
afficher(x=1, y=2, z=3)
# x=1, y=2, z=3
Erreurs courantes à éviter
Voici les erreurs les plus fréquentes lorsque vous travaillez avec **kwargs :
# ❌ Erreur 1 : Oublier ** lors du dépaquetage
def saluer(nom, ville):
print(f"Bonjour {nom} de {ville}")
infos = {"nom": "Alice", "ville": "Paris"}
# saluer(infos) # TypeError !
saluer(**infos) # ✅ Correct
# ❌ Erreur 2 : Clés du dictionnaire non-string
# **kwargs n'accepte que des clés de type string
mauvais_dict = {1: "un", 2: "deux"}
# def test(**kwargs): pass
# test(**mauvais_dict) # TypeError !
# ❌ Erreur 3 : Argument en double
def dire_bonjour(nom, **kwargs):
pass
# dire_bonjour("Alice", nom="Bob") # TypeError: multiple values for argument 'nom'
Les clés d'un dictionnaire dépacqueté avec ** doivent obligatoirement être des chaînes de caractères. Toute autre type de clé provoquera une TypeError.
Questions fréquentes
Quelle est la différence entre *args et **kwargs ?
*args collecte les arguments positionnels supplémentaires dans un tuple, tandis que **kwargs collecte les arguments nommés supplémentaires dans un dict. Par exemple, dans func(1, 2, nom="Alice"), les valeurs 1 et 2 iraient dans args, et nom="Alice" irait dans kwargs. Les deux peuvent être utilisés dans la même fonction, mais *args doit toujours précéder **kwargs.
Le nom "kwargs" est-il obligatoire ?
Non, kwargs est simplement une convention communément adoptée par la communauté Python. Vous pouvez utiliser n'importe quel nom de variable valide après le double astérisque **. Par exemple, **options, **parametres ou **config fonctionneront parfaitement. Cependant, nous vous recommandons fortement de respecter la convention **kwargs pour que votre code soit immédiatement compréhensible par d'autres développeurs.
Peut-on modifier kwargs à l'intérieur d'une fonction ?
Oui, kwargs est un dictionnaire Python classique. Vous pouvez librement ajouter, modifier ou supprimer des clés. Cependant, ces modifications ne seront visibles que dans le scope de la fonction, elles n'affecteront pas les variables de l'appelant. C'est un comportement identique à celui de n'importe quel dict passé comme argument.
Comment apprendre à maîtriser **kwargs et les fonctions Python ?
Pour bien maîtriser **kwargs, il est essentiel de pratiquer avec des exemples concrets : décorateurs, classes avec héritage, fonctions de configuration. Nous vous recommandons de suivre notre formation dédiée à Python sur Believemy, qui couvre en détail les fonctions, les arguments variables, les décorateurs et bien d'autres concepts avancés avec des exercices pratiques.