Python *args et **kwargs expliqués : Fonctions flexibles avec des exemples clairs
Ces étoiles dans les signatures de fonction sont moins mystérieuses qu’elles n’en ont l’air. Découvrez comment *args et **kwargs collectent les arguments, comment les mêmes étoiles les décompressent et comment concevoir des signatures de fonction difficiles à utiliser à mauvais escient.
*argscollecte des arguments de position supplémentaires dans un tuple.**kwargscollecte des arguments de mots-clés supplémentaires dans un dictionnaire.- Les noms ne sont qu’une convention, car ce sont
*et**qui comptent. - Les mêmes étoiles décompressent une liste ou un dictionnaire lorsque vous appelez une fonction.
Tôt ou tard, vous lirez une fonction comme def handler(*args, **kwargs) et vous vous demanderez ce qu’elle accepte. Python *args et **kwargs permettent à une fonction de prendre un nombre flexible d’arguments. Ils alimentent les décorateurs, les wrappers et de nombreuses API de bibliothèque.
Ce guide explique comment ils fonctionnent dans les deux sens, ainsi que les nouvelles façons dont Python vous permet de contrôler la façon dont une fonction est appelée. Nous avons exécuté chaque exemple sur Python 3.13 et le résultat affiché provient de cette exécution.
Que fait *args
Un paramètre écrit avec une étoile collecte tous les arguments de position supplémentaires dans un tuple :
def total(*args):
print(type(args).__name__, args)
return sum(args)
print(total(1, 2, 3))
# tuple (1, 2, 3)
# 6
print(total())
# tuple ()
# 0
Sans arguments, vous obtenez simplement un tuple vide, donc la fonction fonctionne toujours. Le nom args est une convention. *numbers se comporterait de la même manière, et un nom plus descriptif est souvent préférable.
Que fait **kwargs
Deux étoiles collectent tous les arguments de mots-clés supplémentaires dans un dictionnaire :
def describe(**kwargs):
print(type(kwargs).__name__, kwargs)
describe(name="Asha", city="Pune")
# dict {'name': 'Asha', 'city': 'Pune'}
Parce qu’il s’agit d’un dictionnaire normal, vous pouvez le parcourir, rechercher des clés avec get() et le transmettre. Les clés conservent l’ordre dans lequel elles ont été transmises.
Ordre des paramètres
Lorsque vous mélangez différents types de paramètres, Python nécessite un ordre spécifique :
- Paramètres de position réguliers
*arguments- Paramètres de mots clés uniquement
**kwargs
def order(item, qty=1, *extras, priority=False, **notes):
print(item, qty, extras, priority, notes)
order("pen")
# pen 1 () False {}
order("pen", 2, "gift wrap", "card", priority=True, note="leave at door")
# pen 2 ('gift wrap', 'card') True {'note': 'leave at door'}
Remarquez priority. Parce qu’il vient après *extras, il ne peut être défini que par son nom, donc un argument de position parasite ne pourrait jamais le définir par accident. Mettre **kwargs plus tôt n’est pas autorisé, et notre test a généré un SyntaxError : “les arguments ne peuvent pas suivre l’argument var-keyword”.
Déballage lors d’un appel
Les mêmes étoiles fonctionnent en sens inverse sur le site d’appel. Une étoile répartit une liste ou un tuple en arguments de position, et deux étoiles répartissent un dictionnaire en arguments de mots-clés :
nums = [4, 5, 6]
total(*nums) # same as total(4, 5, 6) -> 15
settings = {"name": "Ben", "city": "Delhi"}
describe(**settings) # same as describe(name="Ben", city="Delhi")
Le déballage fonctionne avec n’importe quelle fonction, pas seulement celles qui utilisent *args et **kwargs. C’est pratique lorsque la configuration arrive sous forme de dictionnaire :
def connect(host, port, timeout=5):
return f"{host}:{port} (timeout {timeout})"
connect(**{"host": "db.local", "port": 5432})
# 'db.local:5432 (timeout 5)'
connect(**{"host": "db.local", "port": 5432, "ssl": True})
# TypeError: connect() got an unexpected keyword argument 'ssl'
Cette erreur est utile. Une fonction sans **kwargs rejette les options inconnues, donc une faute de frappe dans le nom d’un paramètre échoue bruyamment au lieu d’être ignorée en silence.
Arguments de mots clés uniquement
Un * nu dans la signature, sans nom, signifie « tout ce qui suit doit être passé par mot-clé » :
def make_user(name, *, admin=False):
return name, admin
make_user("Asha", admin=True)
# ('Asha', True)
make_user("Asha", True)
# TypeError: make_user() takes 1 positional argument but 2 were given
C’est un excellent moyen de protéger les drapeaux booléens. Un appel comme make_user("Asha", True) oblige les lecteurs à deviner ce que signifie True. Exiger admin=True rend chaque appel explicite.
Arguments de position uniquement
Depuis Python 3.8, un / marque les paramètres qui ne peuvent être transmis que par position :
def area(width, height, /):
return width * height
area(3, 4) # 12
area(width=3, height=4)
# TypeError: area() got some positional-only arguments
# passed as keyword arguments: 'width, height'
Cela permet à une bibliothèque de renommer ces paramètres ultérieurement sans casser le code de qui que ce soit, puisque personne ne peut dépendre de leurs noms. Plusieurs éléments intégrés fonctionnent de cette façon. Par exemple, len(obj=[1]) ne fonctionne pas.
Conseil : Le modèle complet est def f(pos_only, /, normal, *, kw_only). Vous avez rarement besoin des trois, mais savoir ce que signifient / et * vous aide à lire la documentation et les signatures de bibliothèque.
Décorateurs et emballages
C’est la principale raison réelle d’utiliser les deux ensemble. Un wrapper doit souvent accepter tous les arguments pris par la fonction d’origine et les transmettre tels quels :
import functools, time
def timed(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.perf_counter() - start:.4f}s")
return result
return wrapper
@timed
def slow_add(a, b, delay=0.01):
time.sleep(delay)
return a + b
print(slow_add(2, 3, delay=0.02))
# slow_add took 0.0205s
# 5
Le wrapper n’a pas du tout besoin de connaître les paramètres de slow_add, il fonctionne donc pour n’importe quelle fonction. Votre chiffre de timing sera légèrement différent. Notre guide des décorateurs Python explique functools.wraps et d’autres détails.
Options de fusion
** fonctionne également dans un littéral de dictionnaire, ce qui offre un moyen pratique d’appliquer des remplacements en plus des valeurs par défaut :
defaults = {"theme": "light", "lang": "en"}
def build(**overrides):
return {**defaults, **overrides}
build(theme="dark")
# {'theme': 'dark', 'lang': 'en'}
Les clés ultérieures gagnent, donc le remplacement remplace la valeur par défaut. Les sous-classes utilisent également ce modèle, comme dans super().__init__(*args, **kwargs), pour transmettre des arguments supplémentaires à une classe parent. Voir Classes et objets Python pour en savoir plus sur super().
Aide-mémoire
| Syntaxe | Où | Signification |
|---|---|---|
déf f(*args) |
Définition | Collectez des arguments de position supplémentaires dans un tuple |
def f(**kwargs) |
Définition | Collectez des arguments de mots clés supplémentaires dans un dict |
déf f(a, *, b) |
Définition | b est réservé aux mots clés |
déf f(a, /, b) |
Définition | a est uniquement positionnel |
f(*items) |
Appeler | Répartir une séquence en arguments de position |
f(**options) |
Appeler | Répartissez un dict dans les arguments de mots clés |
{**a, **b} |
Littéral | Fusionner les dictionnaires |
[*a, *b] |
Littéral | Combiner les itérables dans une liste |
Quand les éviter
La flexibilité a un prix. Une fonction définie comme def create(**kwargs) ne dit rien aux lecteurs sur les options existantes et les éditeurs ne peuvent pas les compléter automatiquement. Les fautes de frappe peuvent traverser silencieusement plusieurs couches avant que quelque chose échoue.
Préférez donc les paramètres explicites lorsque vous savez ce dont une fonction a besoin. Utilisez *args et **kwargs lorsque vous avez réellement besoin de flexibilité : wrappers, décorateurs, transfert d’arguments vers une autre fonction ou une véritable entrée de longueur variable comme total(*numbers). Si une fonction nécessite de nombreuses options nommées, un dataclass peut également être plus clair.
Erreurs courantes
Utiliser une valeur par défaut mutable
Il ne s’agit pas ici d’étoiles, mais elle apparaît souvent à proximité d’elles. Une valeur par défaut est créée une fois, lorsque la fonction est définie, puis partagée entre les appels :
def bad(items=[]):
items.append(1)
return items
print(bad(), bad())
# [1, 1] [1, 1]
Les deux appels ont renvoyé la même liste, qui contenait alors deux éléments. Utilisez items=None et créez une nouvelle liste dans la fonction.
Autres erreurs
- Oublier de déballer. L’appel de
total(nums)transmet une liste plutôt que trois nombres. Écriveztotal(*nums). - Avaler des fautes de frappe. Une fonction qui accepte
**kwargsmais ignore les clés inconnues masque les options mal orthographiées. Validez-les, ou évitez**kwargs. - Passer le même argument deux fois.
f(1, **{"a": 2}), oùaest le premier paramètre, génère une erreur « valeurs multiples ». - Pas de transfert dans un wrapper. L’appel de
func(args, kwargs)transmet un tuple et un dict. La forme correcte estfunc(*args, **kwargs).
Tapez des indices pour *args et **kwargs
Les indications de type sur *args et **kwargs décrivent chaque valeur individuelle, pas la collection entière. Cela surprend beaucoup de gens :
def total(*prices: float) -> float:
return sum(prices) # prices is tuple[float, ...]
def tag(**attrs: str) -> str:
return " ".join(f'{k}="{v}"' for k, v in attrs.items()) # dict[str, str]
total(1.5, 2.5) # 4.0
tag(id="x", role="button") # id="x" role="button"
Ici, prices est traité comme un tuple de flottants et attrs comme un dictionnaire de chaînes. Lorsque les arguments de mots-clés ont des types différents, Python 3.12 a ajouté une option précise : un TypedDict combiné avec Unpack.
from typing import TypedDict, Unpack
class Options(TypedDict, total=False):
timeout: float
retries: int
def fetch(url: str, **opts: Unpack[Options]) -> None: ...
Un vérificateur de type tel que mypy ou Pyright peut alors rejeter fetch(url, retries="3") ou un nom d’option mal orthographié, ce qu’un simple **kwargs ne pourra jamais faire. Le comportement d’exécution ne change cependant pas. Lorsque nous avons exécuté fetch("u", retries="3") sous Python 3.13, il s’est exécuté sans problème, car Python lui-même n’applique pas les indications de type.
Si vous documentez une longue liste de clés acceptées pour **kwargs, envisagez de les remplacer par des paramètres explicites réservés aux mots clés. Les éditeurs peuvent ensuite les compléter automatiquement et les erreurs sont immédiatement détectées.
Pour conclure
Rappelez-vous les deux directions. Dans une définition, les étoiles collectionnent. Lors d’un appel, ils déballent. Ajoutez des arguments de mots-clés uniquement pour rendre les appels plus clairs et réservez **kwargs pour le code qui transmet véritablement les arguments. Pour vous entraîner, écrivez un petit décorateur qui enregistre les arguments de chaque appel et utilisez-le sur quelques fonctions différentes.
Questions fréquentes
Quelle est la différence entre *args et **kwargs ?
*args collecte des arguments de position supplémentaires dans un tuple. **kwargs collecte des arguments de mots-clés supplémentaires dans un dictionnaire. Une fonction peut utiliser les deux ensemble.
Dois-je les appeler des args et des kwargs ?
Non. Les étoiles font le travail, donc les *valeurs ou **options sont également valables. args et kwargs sont simplement une convention largement reconnue.
Dans quel ordre *args et **kwargs doivent-ils être placés ?
Les paramètres réguliers en premier, puis *args, puis les paramètres de mots-clés uniquement et **kwargs en dernier. Tout autre ordre provoque une SyntaxError.
Que signifie un seul * dans la définition d’une fonction ?
Un simple * signifie que chaque paramètre qui le suit doit être transmis par mot-clé. Cela rend les appels plus clairs, en particulier pour les indicateurs booléens.
Que signifie / dans une fonction Python ?
Les paramètres avant / sont uniquement positionnels, les appelants ne peuvent donc pas les transmettre par leur nom. Cette fonctionnalité a été ajoutée dans Python 3.8.
Continuez à apprendre
Besoin d’aide pour les tests, WordPress ou le référencement ?
Nous aidons les équipes à proposer de meilleurs logiciels et à développer leurs sites. Dites-nous sur quoi vous travaillez et nous vous proposerons une prochaine étape claire.
Plus de guides sur tests, IA et développement moderne
Vous avez apprécié cet article ? Choisissez un sujet et accédez directement à nos nouveaux didacticiels.
Les hallucinations de l'IA expliquées : pourquoi les modèles de langage inventent les choses et comment les réduire
Les hallucinations de l'IA ont expliqué : ce qu'elles sont, pourquoi les modèles de langage les produisent, les types courants, comment les réduire …
Lire l'article →DevOpsVariables d'environnement expliquées : fichiers .env, secrets et configuration correctement effectués
Variables d'environnement expliquées avec des exemples Python que nous avons exécutés : lecture et définition, pourquoi chaque valeur est une chaîne…
Lire l'article →Développement logicielLimitation du débit de l'API expliquée : algorithmes, en-têtes et gestion des erreurs 429
Limitation du débit de l'API expliquée avec une simulation que nous avons exécutée : compartiment de jetons, fenêtre fixe, fenêtre glissante, 429 ré…
Lire l'article →Automatisation des testsSe moquer en Python : unittest.mock, patch et les erreurs qui font mentir les tests
Moquerie en Python avec unittest.mock, testée : Mock et return_value, side_effect, correctifs où un nom est utilisé, spec et autospec, dates moqueus…
Lire l'article →Tests logicielsCouverture du code expliquée : couverture des lignes par rapport aux branches et ce que signifie réellement 100 %
Couverture du code expliquée avec une véritable exécution de cover.py : couverture des lignes, des branches et des conditions, lecture d'un rapport,…
Lire l'article →Bases de données et SQLDELETE vs TRUNCATE vs DROP dans SQL : différences, restauration et quand les utiliser
DELETE vs TRUNCATE vs DROP expliqué avec SQL testé : ce que chacun supprime, restauration, déclencheurs, réinitialisation d'identité, vitesse et en …
Lire l'article →Bases de données et SQLVues SQL expliquées : créez, utilisez et évitez les pièges, avec des exemples
Vues SQL expliquées avec les requêtes que nous avons exécutées : création d'une vue, résultats en direct, masquage des colonnes, performances, vues …
Lire l'article →Développement logicielInterface TypeScript vs Type : différences réelles, testées avec le compilateur
Interface TypeScript vs type, vérifiée avec le vrai compilateur : ce que les deux peuvent faire, ce qu'un seul peut faire, la fusion des déclaration…
Lire l'article →Développement logicielPython *args et **kwargs expliqués : fonctions flexibles avec des exemples clairs
Python *args et **kwargs expliqués avec le code que nous avons exécuté : emballage et déballage, ordre des paramètres, arguments de mot-clé uniqueme…
Lire l'article →Développement logicielFonctions Python Lambda : syntaxe, utilisations réelles et quand ne pas les utiliser
Fonctions Python lambda expliquées avec le code que nous avons exécuté : syntaxe, tri avec clé, carte et filtre, fermetures, piège de liaison tardiv…
Lire l'article →Intelligence artificielleQue sont les agents IA ? Comment ils fonctionnent, avec une simple boucle d'agent que vous pouvez exécuter
Que sont les agents IA ? Un guide simple sur la boucle d'agent, les outils, la mémoire et les garde-fous, avec une petite boucle Python que nous avo…
Lire l'article →DevOpsSurveillance vs observabilité : la vraie différence, avec les journaux, les métriques et les traces
Surveillance et observabilité expliquées : échecs connus et inconnus, journaux, métriques et traces, présentation pas à pas des incidents de paiemen…
Lire l'article →DevOpsVolumes Docker vs montages Bind : conserver les données du conteneur de la bonne manière
Volumes Docker et montages de liaison expliqués : pourquoi les données du conteneur disparaissent, volumes nommés, montages de liaison, tmpfs, exemp…
Lire l'article →Automatisation des testsTDD vs BDD : principales différences, une fonctionnalité conçue dans les deux sens et quand les utiliser
TDD vs BDD expliqué avec une fonctionnalité de remise construite dans les deux sens : refactor rouge-vert avec le code que nous avons exécuté, scéna…
Lire l'article →Développement logicielGit merge vs rebase : différences, exemples et quand utiliser chacun
Git merge vs rebase expliqué avec de vraies sorties de log, un tableau comparatif, la règle d'or du rebase et un flux de travail sûr pour les équipes.
Lire l'article →Développement logicielList comprehension Python : syntaxe, exemples et cas à éviter
La list comprehension Python expliquée : syntaxe, filtres, if-else, boucles imbriquées, temps mesurés et cas où une boucle for classique est préféra…
Lire l'article →Bases de données et SQLJointures SQL expliquées avec exemples (Inner, Left, Right, Full)
Comprenez les jointures SQL pas à pas avec des tables et requêtes d'exemple : INNER, LEFT, RIGHT, FULL, CROSS et SELF, plus erreurs courantes et con…
Lire l'article →