Python *args et **kwargs Expliqués : Le Guide Complet
Updated on
Tout développeur Python se heurte à ce mur à un moment ou à un autre. Vous ouvrez le code source d'une bibliothèque, une pull request d'un collègue, ou un projet open-source, et vous voyez des signatures de fonctions parsemées de *args et **kwargs. Que signifient les astérisques ? Pourquoi y en a-t-il un et deux ? Quand faut-il utiliser l'un ou l'autre ? Les utiliser incorrectement produit des messages TypeError déroutants comme "takes 2 positional arguments but 5 were given" ou "got an unexpected keyword argument," et soudainement, un simple appel de fonction se transforme en session de débogage.
Le problème s'aggrave quand vous devez écrire vous-même des fonctions flexibles. Le codage en dur de chaque paramètre rend votre API rigide. Accepter trop de paramètres nommés rend la signature de la fonction illisible. Vous avez besoin d'un moyen d'écrire des fonctions qui acceptent un nombre variable d'arguments sans sacrifier la clarté.
Python résout cela avec deux fonctionnalités syntaxiques spéciales : *args pour les arguments positionnels variables et **kwargs pour les arguments par mot-clé variables. Ce guide explique les deux depuis les bases, couvre les opérateurs d'unpacking, passe en revue les patterns du monde réel et vous aide à éviter les erreurs les plus courantes.
Que sont *args et **kwargs ?
En Python, *args et **kwargs sont des conventions pour accepter un nombre variable d'arguments dans les définitions de fonctions.
*argscollecte les arguments positionnels supplémentaires dans un tuple.**kwargscollecte les arguments par mot-clé supplémentaires dans un dictionary.
Les noms args et kwargs sont des conventions, pas des obligations. La magie vient des préfixes * et **, pas des noms eux-mêmes. Vous pourriez écrire *values et **options et ils fonctionneraient de manière identique.
Voici la démonstration la plus simple :
def show_args(*args, **kwargs):
print(f"args = {args}")
print(f"kwargs = {kwargs}")
show_args(1, 2, 3, name="Alice", age=30)
# args = (1, 2, 3)
# kwargs = {'name': 'Alice', 'age': 30}Les arguments positionnels (valeurs passées sans noms) atterrissent dans args sous forme de tuple. Les arguments par mot-clé (valeurs passées avec la syntaxe key=value) atterrissent dans kwargs sous forme de dictionary. C'est l'ensemble du concept de base.
Comprendre *args : Arguments Positionnels Variables
L'astérisque simple * avant un nom de paramètre indique à Python de packer tous les arguments positionnels restants dans un tuple. Cela permet à votre fonction d'accepter n'importe quel nombre de valeurs positionnelles.
Syntaxe de Base et Utilisation
def add_all(*args):
"""Sum any number of values."""
total = 0
for num in args:
total += num
return total
print(add_all(1, 2)) # 3
print(add_all(1, 2, 3, 4, 5)) # 15
print(add_all(10)) # 10
print(add_all()) # 0À l'intérieur de la fonction, args est un tuple Python ordinaire. Vous pouvez itérer dessus, y accéder par index, vérifier sa longueur, et le passer à d'autres fonctions.
def describe_args(*args):
print(f"Type: {type(args)}")
print(f"Length: {len(args)}")
print(f"First element: {args[0] if args else 'N/A'}")
print(f"Contents: {args}")
describe_args("hello", 42, True)
# Type: <class 'tuple'>
# Length: 3
# First element: hello
# Contents: ('hello', 42, True)Combiner les Paramètres Réguliers avec *args
Vous pouvez mélanger les paramètres standard avec *args. Tous les paramètres positionnels réguliers sont remplis d'abord, et tous les arguments positionnels restants vont dans *args :
def log_message(level, *args):
"""Log a message with a severity level."""
message = " ".join(str(a) for a in args)
print(f"[{level.upper()}] {message}")
log_message("info", "Server started on port", 8080)
# [INFO] Server started on port 8080
log_message("error", "Connection failed:", "timeout after", 30, "seconds")
# [ERROR] Connection failed: timeout after 30 secondsExemple du Monde Réel : Une Fonction de Moyenne Flexible
def average(*values):
"""Calculate the average of any number of values."""
if not values:
raise ValueError("average() requires at least one argument")
return sum(values) / len(values)
print(average(85, 90, 78)) # 84.33333333333333
print(average(100)) # 100.0
print(average(72, 88, 95, 67, 91)) # 82.6Exemple du Monde Réel : Assistant de Formatage de Chaînes
def build_path(*segments):
"""Join path segments with forward slashes, stripping extras."""
cleaned = [seg.strip("/") for seg in segments if seg]
return "/" + "/".join(cleaned)
print(build_path("api", "v2", "users", "123"))
# /api/v2/users/123
print(build_path("/data/", "/reports/", "2026/", "sales.csv"))
# /data/reports/2026/sales.csvComprendre **kwargs : Arguments par Mot-clé Variables
Le double astérisque ** avant un nom de paramètre indique à Python de packer tous les arguments par mot-clé restants dans un dictionary. Cela permet à votre fonction d'accepter n'importe quel nombre de valeurs nommées.
Syntaxe de Base et Utilisation
def print_info(**kwargs):
"""Print key-value pairs in a formatted way."""
for key, value in kwargs.items():
print(f" {key}: {value}")
print_info(name="Alice", age=30, city="Seattle")
# name: Alice
# age: 30
# city: SeattleÀ l'intérieur de la fonction, kwargs est un dictionary Python standard. Vous pouvez utiliser .get(), .keys(), .values(), .items(), et toute autre méthode de dict.
def describe_kwargs(**kwargs):
print(f"Type: {type(kwargs)}")
print(f"Keys: {list(kwargs.keys())}")
print(f"Values: {list(kwargs.values())}")
describe_kwargs(x=10, y=20, z=30)
# Type: <class 'dict'>
# Keys: ['x', 'y', 'z']
# Values: [10, 20, 30]Exemple du Monde Réel : Constructeur de Configuration
def create_connection(host, port, **kwargs):
"""Create a database connection with optional settings."""
config = {
"host": host,
"port": port,
"timeout": kwargs.get("timeout", 30),
"retries": kwargs.get("retries", 3),
"ssl": kwargs.get("ssl", True),
"pool_size": kwargs.get("pool_size", 5),
}
# Add any extra settings the caller provided
for key, value in kwargs.items():
if key not in config:
config[key] = value
return config
# Basic usage
basic = create_connection("localhost", 5432)
print(basic)
# {'host': 'localhost', 'port': 5432, 'timeout': 30, 'retries': 3, 'ssl': True, 'pool_size': 5}
# With custom options
custom = create_connection(
"db.example.com", 5432,
timeout=60,
ssl=False,
application_name="my_app"
)
print(custom)
# {'host': 'db.example.com', 'port': 5432, 'timeout': 60, 'retries': 3, 'ssl': False, 'pool_size': 5, 'application_name': 'my_app'}Exemple du Monde Réel : Constructeur de Balises HTML
def html_tag(tag, content="", **attributes):
"""Generate an HTML tag with optional attributes."""
attr_str = ""
for key, value in attributes.items():
# Convert Python naming to HTML (class_ -> class)
html_key = key.rstrip("_")
attr_str += f' {html_key}="{value}"'
if content:
return f"<{tag}{attr_str}>{content}</{tag}>"
return f"<{tag}{attr_str} />"
print(html_tag("a", "Click here", href="https://example.com", class_="btn"))
# <a href="https://example.com" class="btn">Click here</a>
print(html_tag("img", src="photo.jpg", alt="A photo", width="200"))
# <img src="photo.jpg" alt="A photo" width="200" />
print(html_tag("p", "Hello world", id="intro", style="color: blue"))
# <p id="intro" style="color: blue">Hello world</p>Utiliser *args et **kwargs Ensemble
Vous pouvez utiliser les deux dans la même définition de fonction pour accepter n'importe quelle combinaison d'arguments positionnels et par mot-clé. L'ordre des paramètres suit des règles strictes.
Règles d'Ordre des Paramètres
Python impose cet ordre exact dans les signatures de fonctions :
- Paramètres positionnels réguliers
*args(positionnel variable)- Paramètres keyword-only (après
*args) **kwargs(par mot-clé variable)
def example(a, b, *args, option=True, **kwargs):
print(f"a = {a}")
print(f"b = {b}")
print(f"args = {args}")
print(f"option = {option}")
print(f"kwargs = {kwargs}")
example(1, 2, 3, 4, 5, option=False, color="red", size=10)
# a = 1
# b = 2
# args = (3, 4, 5)
# option = False
# kwargs = {'color': 'red', 'size': 10}Voici un résumé des types de paramètres et de leur ordre :
| Position | Type | Syntaxe | Exemple | Description |
|---|---|---|---|---|
| 1er | Positionnel | param | a, b | Requis, rempli par position |
| 2ème | Par défaut | param=value | c=10 | Optionnel, rempli par position ou nom |
| 3ème | Positionnel variable | *args | *args | Collecte les arguments positionnels supplémentaires |
| 4ème | Keyword-only | param (après *) | option=True | Doit être passé par nom |
| 5ème | Par mot-clé variable | **kwargs | **kwargs | Collecte les arguments par mot-clé supplémentaires |
Pattern Commun : Fonction de Pass-Through
L'un des patterns les plus utiles avec *args et **kwargs est de créer des fonctions qui passent tous les arguments à une autre fonction :
def timed_call(func, *args, **kwargs):
"""Call a function and measure its execution time."""
import time
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} took {elapsed:.4f}s")
return result
def expensive_sum(a, b, c):
import time
time.sleep(0.1)
return a + b + c
result = timed_call(expensive_sum, 10, 20, c=30)
# expensive_sum took 0.1003s
print(result)
# 60L'Unpacking avec * et **
Les opérateurs * et ** fonctionnent dans les deux sens : ils packent les arguments dans les définitions de fonctions, et ils unpackent les arguments dans les appels de fonctions et autres contextes.
Unpacking de Listes et Tuples avec *
Utilisez * pour unpack un itérable en arguments positionnels individuels :
def add(a, b, c):
return a + b + c
numbers = [10, 20, 30]
# Without unpacking - this causes TypeError
# add(numbers) # TypeError: add() missing 2 required positional arguments
# With unpacking - spreads list into separate arguments
result = add(*numbers)
print(result) # 60
# Works with tuples, sets, and any iterable
coords = (5, 10, 15)
print(add(*coords)) # 30Vous pouvez aussi utiliser * pour l'unpacking dans les assignations et la construction de listes (Python 3.5+) :
# Extended unpacking in assignments
first, *middle, last = [1, 2, 3, 4, 5]
print(first) # 1
print(middle) # [2, 3, 4]
print(last) # 5
# Unpacking in list/tuple construction
list_a = [1, 2, 3]
list_b = [4, 5, 6]
combined = [*list_a, *list_b]
print(combined) # [1, 2, 3, 4, 5, 6]
# Unpacking with additional elements
extended = [0, *list_a, 99, *list_b, 100]
print(extended) # [0, 1, 2, 3, 99, 4, 5, 6, 100]Unpacking de Dictionaries avec **
Utilisez ** pour unpack un dictionary en arguments par mot-clé :
def create_user(name, email, role="viewer"):
return {"name": name, "email": email, "role": role}
user_data = {"name": "Alice", "email": "alice@example.com", "role": "admin"}
# Unpack dict into keyword arguments
user = create_user(**user_data)
print(user)
# {'name': 'Alice', 'email': 'alice@example.com', 'role': 'admin'}Fusion de Dictionaries avec **
L'un des usages les plus pratiques de ** est la fusion de dictionaries :
defaults = {"color": "blue", "size": 12, "font": "Arial"}
user_prefs = {"color": "red", "size": 16}
# Merge: user_prefs override defaults
merged = {**defaults, **user_prefs}
print(merged)
# {'color': 'red', 'size': 16, 'font': 'Arial'}
# Python 3.9+ also supports the | operator
merged_new = defaults | user_prefs
print(merged_new)
# {'color': 'red', 'size': 16, 'font': 'Arial'}
# Adding extra keys during merge
final = {**defaults, **user_prefs, "theme": "dark"}
print(final)
# {'color': 'red', 'size': 16, 'font': 'Arial', 'theme': 'dark'}Combiner l'Unpacking avec * et **
Vous pouvez utiliser les deux opérateurs ensemble lors d'un appel de fonction :
def report(title, *items, separator="---", **metadata):
print(f"== {title} ==")
for item in items:
print(f" - {item}")
print(separator)
for key, value in metadata.items():
print(f" {key}: {value}")
positional = ["Task A", "Task B", "Task C"]
options = {"author": "Alice", "date": "2026-02-14"}
report("Sprint Review", *positional, separator="===", **options)
# == Sprint Review ==
# - Task A
# - Task B
# - Task C
# ===
# author: Alice
# date: 2026-02-14Patterns Pratiques
Pattern 1 : Fonctions Décorateur
L'utilisation la plus courante de *args et **kwargs en Python production est l'écriture de décorateurs. Un décorateur enveloppe une fonction avec une autre. Comme vous ne connaissez pas la signature de la fonction enveloppée à l'avance, vous devez utiliser *args et **kwargs pour transférer tous les arguments :
import functools
import time
def retry(max_attempts=3, delay=1.0):
"""Retry a function up to max_attempts times on failure."""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
print(f"Attempt {attempt}/{max_attempts} failed: {e}")
if attempt < max_attempts:
time.sleep(delay)
raise last_exception
return wrapper
return decorator
@retry(max_attempts=3, delay=0.5)
def fetch_data(url, timeout=10):
"""Simulate fetching data that might fail."""
import random
if random.random() < 0.6:
raise ConnectionError(f"Failed to connect to {url}")
return f"Data from {url}"
# The decorator forwards url and timeout through *args/**kwargs
result = fetch_data("https://api.example.com", timeout=5)
print(result)Pattern 2 : Transmission de init dans les Sous-classes
Lors du sous-classement, vous devez souvent transférer les arguments du constructeur à la classe parente. *args et **kwargs rendent cela propre :
class Animal:
def __init__(self, name, species, sound="..."):
self.name = name
self.species = species
self.sound = sound
def speak(self):
return f"{self.name} says {self.sound}"
class Dog(Animal):
def __init__(self, *args, breed="Unknown", **kwargs):
super().__init__(*args, **kwargs)
self.breed = breed
def info(self):
return f"{self.name} ({self.breed}) - {self.species}"
# All Animal parameters pass through seamlessly
dog = Dog("Rex", "Canine", sound="Woof!", breed="German Shepherd")
print(dog.speak()) # Rex says Woof!
print(dog.info()) # Rex (German Shepherd) - CanineCe pattern est essentiel quand vous travaillez avec des hiérarchies de classes complexes, surtout dans des frameworks comme Django, Flask, ou SQLAlchemy où vous étendez des classes de base.
Pattern 3 : Fonctions Wrapper et Proxy
Quand vous avez besoin d'intercepter ou modifier des appels de fonction sans changer la fonction originale :
def log_call(func):
"""Log every call to a function with its arguments."""
@functools.wraps(func)
def wrapper(*args, **kwargs):
args_repr = [repr(a) for a in args]
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
signature = ", ".join(args_repr + kwargs_repr)
print(f"Calling {func.__name__}({signature})")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result!r}")
return result
return wrapper
@log_call
def calculate_discount(price, discount_pct, tax_rate=0.08):
discounted = price * (1 - discount_pct / 100)
return round(discounted * (1 + tax_rate), 2)
calculate_discount(100, 20, tax_rate=0.1)
# Calling calculate_discount(100, 20, tax_rate=0.1)
# calculate_discount returned 88.0Pattern 4 : Constructeurs de Client API
Construire des wrappers d'API flexibles est un cas d'usage classique :
import json
class APIClient:
def __init__(self, base_url, **default_headers):
self.base_url = base_url.rstrip("/")
self.default_headers = {
"Content-Type": "application/json",
"Accept": "application/json",
**default_headers,
}
def request(self, method, endpoint, *args, **kwargs):
"""Build a request with merged headers and parameters."""
url = f"{self.base_url}/{endpoint.lstrip('/')}"
headers = {**self.default_headers, **kwargs.pop("headers", {})}
request_info = {
"method": method,
"url": url,
"headers": headers,
**kwargs,
}
print(json.dumps(request_info, indent=2, default=str))
return request_info
def get(self, endpoint, **kwargs):
return self.request("GET", endpoint, **kwargs)
def post(self, endpoint, **kwargs):
return self.request("POST", endpoint, **kwargs)
# Usage
client = APIClient(
"https://api.example.com",
Authorization="Bearer token123"
)
client.get("/users", params={"page": 1, "limit": 50})
client.post("/users", data={"name": "Alice"}, headers={"X-Request-ID": "abc123"})Pattern 5 : Data Science -- Paramètres de Tracé Dynamiques
Quand vous construisez des fonctions d'analyse de données, **kwargs vous permet de passer la configuration aux bibliothèques sous-jacentes :
import pandas as pd
def analyze_column(df, column, **plot_kwargs):
"""Analyze a dataframe column and generate summary statistics."""
stats = {
"count": df[column].count(),
"mean": df[column].mean(),
"std": df[column].std(),
"min": df[column].min(),
"max": df[column].max(),
}
print(f"\nAnalysis of '{column}':")
for stat, value in stats.items():
print(f" {stat}: {value:.2f}")
# Forward any extra kwargs to the plot function
plot_defaults = {"kind": "hist", "bins": 20, "title": f"Distribution of {column}"}
plot_config = {**plot_defaults, **plot_kwargs}
# df[column].plot(**plot_config) # Uncomment with matplotlib installed
print(f" Plot config: {plot_config}")
return stats
# Create sample data
df = pd.DataFrame({
"revenue": [100, 250, 180, 320, 275, 410, 195, 360],
"quantity": [5, 12, 8, 15, 13, 20, 9, 17],
})
# Default analysis
analyze_column(df, "revenue")
# Custom plot settings passed through **kwargs
analyze_column(df, "revenue", kind="box", color="steelblue", figsize=(10, 6))Si vous travaillez dans des notebooks Jupyter et souhaitez expérimenter avec des signatures de fonctions de manière interactive, RunCell (opens in a new tab) fournit un environnement notebook alimenté par l'IA où vous pouvez tester des patterns avec *args et **kwargs, obtenir des suggestions en temps réel pour la gestion des paramètres, et déboguer des problèmes de passage d'arguments sans quitter votre workflow.
Erreurs Courantes et Comment les Corriger
Voici les erreurs les plus fréquentes que les développeurs Python rencontrent avec *args et **kwargs, ainsi que leurs solutions :
| Erreur | Message d'Erreur | Cause | Correction |
|---|---|---|---|
| Mauvais ordre des paramètres | SyntaxError: invalid syntax | Placer **kwargs avant *args | Toujours utiliser l'ordre : régulier, *args, keyword-only, **kwargs |
| Passer une liste au lieu d'unpacker | TypeError: func() missing required arguments | Passer [1,2,3] au lieu de *[1,2,3] | Utiliser * pour unpack : func(*my_list) |
| Argument par mot-clé dupliqué | TypeError: got multiple values for argument 'x' | Même clé dans les arguments positionnels et **kwargs | Assurer qu'il n'y a pas de chevauchement entre arguments positionnels et clés de dict |
| Modification directe de kwargs | Effets de bord inattendus | Mutation du dict kwargs | Utiliser kwargs.copy() ou {**kwargs, ...} |
Oublier *args dans super().init | TypeError: __init__() missing arguments | Ne pas transférer les args à la classe parente | Utiliser super().__init__(*args, **kwargs) |
| Utilisation de valeur par défaut mutable avec kwargs | État partagé entre appels | def func(data={}) | Utiliser None comme valeur par défaut : def func(data=None) |
Exemple : Argument par Mot-clé Dupliqué
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
data = {"name": "Alice", "greeting": "Hi"}
# WRONG: name est passé à la fois comme positionnel ET dans **data
# greet("Alice", **data)
# TypeError: greet() got multiple values for argument 'name'
# CORRECT: passer uniquement via unpacking
print(greet(**data))
# Hi, Alice!
# OR: supprimer la clé dupliquée
print(greet("Alice", **{"greeting": "Hi"}))
# Hi, Alice!Exemple : Modification Sécurisée de kwargs
def process(name, **kwargs):
# WRONG: modifier kwargs directement affecte le dict de l'appelant
# kwargs["processed"] = True
# CORRECT: créer un nouveau dict
config = {**kwargs, "processed": True}
return {"name": name, **config}
settings = {"timeout": 30, "retries": 3}
result = process("task1", **settings)
print(result)
# {'name': 'task1', 'timeout': 30, 'retries': 3, 'processed': True}
# Original dict is unchanged
print(settings)
# {'timeout': 30, 'retries': 3}*args/**kwargs vs Autres Approches
Quand devriez-vous utiliser *args et **kwargs au lieu des alternatives ? Voici une comparaison :
| Approche | Syntaxe | Idéal Pour | Inconvénients |
|---|---|---|---|
*args | def f(*args) | Nombre inconnu de valeurs positionnelles de même type | Pas de type hints par argument, pas d'accès par nom |
**kwargs | def f(**kwargs) | Options flexibles, pass-through vers d'autres fonctions | Pas d'auto-complétion dans les IDEs, pas de vérification de type statique |
| Paramètres explicites | def f(a, b, c) | Ensemble connu et fixe d'arguments | Rigide ; ajouter des paramètres casse les appels existants |
| Paramètres par défaut | def f(a, b=10) | Paramètres optionnels avec valeurs par défaut sensées | Nécessite toujours de connaître toutes les options à l'avance |
| Paramètre liste/tuple | def f(items: list) | Collection ordonnée de valeurs | L'appelant doit construire la liste explicitement |
| Paramètre dict | def f(options: dict) | Configuration structurée | L'appelant doit construire le dict explicitement |
| TypedDict/dataclass | def f(config: Config) | Configuration structurée et type-safe | Plus de boilerplate, nécessite une définition de classe |
Lignes directrices générales :
- Utilisez les paramètres explicites quand l'ensemble des arguments est connu et stable.
- Utilisez
*argsquand votre fonction opère naturellement sur un nombre variable de valeurs de même type (commeprint(),max(),min()). - Utilisez
**kwargsquand vous avez besoin de transférer des options à une autre fonction, accepter une configuration flexible, ou construire des API extensibles. - Utilisez les deux quand vous écrivez des décorateurs, des fonctions proxy, ou des hiérarchies de classes qui nécessitent un transfert complet des arguments.
- Utilisez TypedDict ou dataclass quand vous voulez l'auto-complétion IDE et la vérification de type statique pour une configuration structurée.
Ajouter des Type Hints avec *args et **kwargs
Python 3.11+ permet d'ajouter des type hints en utilisant *args et **kwargs avec Unpack et TypedDict :
# Basic type hints (all args same type)
def add_numbers(*args: float) -> float:
return sum(args)
def set_options(**kwargs: str) -> dict[str, str]:
return kwargs
# Python 3.11+: precise kwargs typing with TypedDict
from typing import TypedDict, Unpack
class ConnectionOptions(TypedDict, total=False):
timeout: int
retries: int
ssl: bool
def connect(host: str, port: int, **kwargs: Unpack[ConnectionOptions]) -> dict:
return {"host": host, "port": port, **kwargs}
# IDE now knows the valid keyword arguments
result = connect("localhost", 5432, timeout=30, ssl=True)
print(result)
# {'host': 'localhost', 'port': 5432, 'timeout': 30, 'ssl': True}Questions Fréquemment Posées
Que signifient *args et **kwargs ?
Les noms args et kwargs sont des raccourcis pour "arguments" et "keyword arguments" respectivement. Ce sont purement des noms conventionnels -- la magie vient des opérateurs préfixes * et **. L'astérisque simple * indique à Python de packer les arguments positionnels supplémentaires dans un tuple, tandis que le double astérisque ** pack les arguments par mot-clé supplémentaires dans un dictionary. Vous verrez ces noms utilisés dans pratiquement chaque codebase, tutoriel et bibliothèque Python car ce sont les conventions universellement reconnues, mais le langage n'exige pas ces noms spécifiques.
Puis-je utiliser d'autres noms que args et kwargs ?
Oui. Les noms args et kwargs sont des conventions, pas des exigences syntaxiques. Le comportement d'unpacking vient entièrement des préfixes * et **. Vous pouvez écrire *values, *items, *numbers, **options, **config, **params, ou tout autre identifiant Python valide. Cependant, s'en tenir à *argset**kwargsest fortement recommandé dans la plupart des cas car tout développeur Python les reconnaît immédiatement. Utilisez des noms personnalisés uniquement quand un nom plus descriptif améliore réellement la lisibilité, comme*pathsdans une fonction de gestion de fichiers ou**headers` dans un client HTTP.
Quel est l'ordre correct des paramètres dans une fonction Python ?
Python impose un ordre strict : les paramètres positionnels réguliers d'abord, puis *args, ensuite les paramètres keyword-only (avec valeurs par défaut), et enfin **kwargs. L'ordre complet est : def func(pos1, pos2, default1=val, *args, kw_only1, kw_only2=val, **kwargs). Violer cet ordre produit une SyntaxError. Un moyen mnémotechnique utile est "positionnel, star-args, keyword-only, double-star-kwargs" -- le nombre d'étoiles augmente de gauche à droite.
Quand dois-je utiliser *args vs un paramètre liste ?
Utilisez *args quand chaque argument est une valeur indépendante séparée et que l'appelant doit les passer naturellement sans construire de conteneur : print("a", "b", "c") est plus naturel que print(["a", "b", "c"]). Utilisez un paramètre liste quand les valeurs forment logiquement une collection que l'appelant a déjà dans une variable, ou quand vous devez distinguer entre la collection et les autres paramètres. Les fonctions built-in comme max(), min(), et print() utilisent *args car la convention d'appel semble naturelle, tandis que des fonctions comme sorted(iterable) prennent un seul itérable car l'entrée est intrinsèquement une séquence.
Est-ce que *args et **kwargs sont lents ?
Le overhead de *args et **kwargs est minimal. Python crée un tuple pour *args et un dictionary pour **kwargs à chaque appel, ce qui implique de petites allocations mémoire. Dans les benchmarks, la différence comparée aux paramètres explicites est typiquement de quelques centaines de nanosecondes par appel -- insignifiant pour pratiquement tout code du monde réel. Vous auriez besoin de millions d'appels dans une boucle serrée avant que ce overhead ne devienne mesurable. Concentrez-vous sur l'efficacité algorithmique et l'optimisation des I/O plutôt que d'éviter *args/**kwargs. La flexibilité et la maintenabilité du code qu'ils fournissent l'emportent largement sur tout coût de micro-performance.
Conclusion
Les *args et **kwargs de Python sont deux des fonctionnalités les plus pratiques du langage. Ils résolvent un problème fondamental : comment écrire des fonctions assez flexibles pour accepter un nombre variable d'arguments sans sacrifier la lisibilité.
Les points clés à retenir :
*argscollecte les arguments positionnels supplémentaires dans un tuple. Utilisez-le quand votre fonction doit accepter n'importe quel nombre de valeurs.**kwargscollecte les arguments par mot-clé supplémentaires dans un dictionary. Utilisez-le pour transférer des options à une autre fonction, accepter une configuration flexible, ou construire des API extensibles.- L'ordre des paramètres est toujours : régulier,
*args, keyword-only,**kwargs. - L'unpacking avec
*et**fonctionne dans les appels de fonctions, la construction de listes et la fusion de dictionaries. - Les décorateurs sont le cas d'usage le plus important du monde réel -- ils dépendent de
*argset**kwargspour envelopper n'importe quelle fonction indépendamment de sa signature.
Commencez avec des paramètres explicites pour les fonctions avec des signatures connues et stables. Appelez *args et **kwargs quand vous avez besoin de flexibilité : décorateurs, transmission de sous-classe, fonctions wrapper et constructeurs d'API. Une fois que vous aurez intégré les mécanismes de packing et unpacking, vous vous surprendrez à écrire du code Python plus propre et plus réutilisable.