Définition de *args en Python
En Python, *args est une convention syntaxique utilisée dans la définition d'une def pour permettre à cette fonction d'accepter un nombre variable d'arguments positionnels. L'astérisque * est l'opérateur de décompression (unpacking), et args est simplement un nom conventionnel — vous pourriez techniquement utiliser n'importe quel autre nom, comme *valeurs ou *elements, mais la convention *args est universellement adoptée par la communauté Python.
Lorsque vous utilisez *args dans la signature d'une fonction, Python regroupe automatiquement tous les arguments positionnels supplémentaires dans un tuple. Cela signifie que vous pouvez appeler votre fonction avec zéro, un, dix ou même cent arguments sans modifier sa définition.
Si vous souhaitez maîtriser ce concept et bien d'autres fondamentaux du langage, nous vous recommandons de suivre notre formation complète sur Python qui couvre en profondeur les mécanismes de fonctions avancées.
*args collecte les arguments positionnels sous forme de tuple. Pour collecter des arguments nommés (mot-clé), on utilise **kwargs (keyword arguments).
Syntaxe de base de *args
La syntaxe de *args est simple : il suffit de placer un astérisque * devant le nom du paramètre dans la définition de la fonction.
def ma_fonction(*args):
print(type(args)) # <class 'tuple'>
for arg in args:
print(arg)
ma_fonction(1, 2, 3)
# 1
# 2
# 3Dans cet exemple, les trois arguments 1, 2 et 3 sont automatiquement regroupés dans le tuple (1, 2, 3) accessible via la variable args.
Exemples pratiques d'utilisation de *args
Fonction de somme avec un nombre variable d'arguments
L'un des cas d'utilisation les plus classiques de *args est la création d'une fonction capable d'additionner un nombre quelconque de valeurs :
def somme(*args):
total = 0
for nombre in args:
total += nombre
return total
print(somme(1, 2)) # 3
print(somme(10, 20, 30)) # 60
print(somme(5)) # 5
print(somme()) # 0Remarquez que la fonction fonctionne même sans argument : dans ce cas, args est simplement un tuple vide ().
Combiner des paramètres classiques et *args
Vous pouvez parfaitement combiner des paramètres classiques avec *args. Les paramètres classiques sont remplis en premier, et le reste des arguments est capturé par *args :
def saluer(salutation, *args):
for nom in args:
print(f"{salutation}, {nom} !")
saluer("Bonjour", "Alice", "Bob", "Charlie")
# Bonjour, Alice !
# Bonjour, Bob !
# Bonjour, Charlie !Ici, "Bonjour" est capturé par le paramètre salutation, et les trois noms suivants sont regroupés dans args sous la forme ("Alice", "Bob", "Charlie"). Notez l'utilisation des f-string pour formater la sortie.
Utiliser *args pour transmettre des arguments à une autre fonction
Un usage très courant de *args est la transmission d'arguments (forwarding) d'une fonction à une autre. C'est particulièrement utile pour créer des fonctions enveloppantes (wrappers) ou des décorateurs :
def journaliser(func, *args):
print(f"Appel de {func.__name__} avec les arguments : {args}")
resultat = func(*args)
print(f"Résultat : {resultat}")
return resultat
def multiplier(a, b):
return a * b
journaliser(multiplier, 4, 5)
# Appel de multiplier avec les arguments : (4, 5)
# Résultat : 20Observez que *args est utilisé deux fois : une première fois pour collecter les arguments dans le tuple args, et une seconde fois avec func(*args) pour décompresser ce tuple et passer chaque élément comme argument individuel à func.
Créer un décorateur avec *args et **kwargs
Les décorateurs sont l'un des cas d'utilisation les plus puissants de *args. En combinant *args et **kwargs, vous pouvez créer un décorateur qui fonctionne avec n'importe quelle fonction, peu importe sa signature :
import time
def mesurer_temps(func):
def wrapper(*args, **kwargs):
debut = time.time()
resultat = func(*args, **kwargs)
fin = time.time()
print(f"{func.__name__} a pris {fin - debut:.4f} secondes")
return resultat
return wrapper
@mesurer_temps
def calculer_somme(*args):
return sum(args)
calculer_somme(1, 2, 3, 4, 5)
# calculer_somme a pris 0.0000 secondesDécompression d'une liste ou d'un tuple avec *
L'opérateur * ne sert pas uniquement dans les définitions de fonctions. Vous pouvez aussi l'utiliser pour décompresser une list ou un tuple lors de l'appel d'une fonction :
def afficher_coordonnees(x, y, z):
print(f"x={x}, y={y}, z={z}")
coordonnees = [10, 20, 30]
afficher_coordonnees(*coordonnees)
# x=10, y=20, z=30
# Fonctionne aussi avec un tuple
point = (5, 15, 25)
afficher_coordonnees(*point)
# x=5, y=15, z=25Décompression étendue dans les assignations
Depuis Python 3, l'opérateur * peut également être utilisé dans les assignations de variables pour capturer plusieurs éléments :
premier, *milieu, dernier = [1, 2, 3, 4, 5]
print(premier) # 1
print(milieu) # [2, 3, 4]
print(dernier) # 5
tete, *queue = "Python"
print(tete) # P
print(queue) # ['y', 't', 'h', 'o', 'n']Attention : dans une assignation avec *, la variable étoilée reçoit toujours une liste (et non un tuple comme dans *args).
Ordre des paramètres dans une fonction Python
Lorsque vous combinez différents types de paramètres, l'ordre est strict et doit être respecté. Voici l'ordre attendu par Python :
| Position | Type de paramètre | Exemple |
|---|---|---|
| 1 | Paramètres positionnels classiques | a, b |
| 2 | *args | *args |
| 3 | Paramètres nommés obligatoires (après *) | clef |
| 4 | **kwargs | **kwargs |
Voici un exemple illustrant cet ordre complet :
def fonction_complete(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}")
fonction_complete(1, 2, 3, 4, 5, option=False, nom="test", valeur=42)
# a = 1
# b = 2
# args = (3, 4, 5)
# option = False
# kwargs = {'nom': 'test', 'valeur': 42}Ne placez jamais *args avant les paramètres positionnels classiques, car cela provoquerait une erreur ou un comportement inattendu.
Différences entre *args et **kwargs
Il est essentiel de bien comprendre la distinction entre *args et **kwargs, car ces deux mécanismes sont complémentaires :
| Caractéristique | *args | **kwargs |
|---|---|---|
| Collecte | Arguments positionnels | Arguments nommés (mot-clé) |
| Type résultant | tuple | dict |
| Syntaxe d'appel | f(1, 2, 3) | f(a=1, b=2) |
| Décompression | *mon_tuple | **mon_dict |
Les deux peuvent être utilisés ensemble, comme nous l'avons vu dans l'exemple du décorateur. Ensemble, ils permettent de créer des fonctions extrêmement flexibles capables de recevoir n'importe quelle combinaison d'arguments. Le dict résultant de **kwargs est particulièrement utile pour traiter des options de configuration.
Bonnes pratiques avec *args
Voici les recommandations à suivre pour utiliser *args de manière efficace et maintenable :
- Respectez la convention de nommage : utilisez
*argset non un autre nom, sauf si un nom plus descriptif améliore réellement la lisibilité (comme*nombrespour une fonction mathématique). - Documentez votre fonction : lorsque vous utilisez
*args, ajoutez une docstring claire expliquant quels types d'arguments sont attendus et en quelle quantité. - Validez les entrées : puisque
*argsaccepte tout, pensez à vérifier le type et le nombre des arguments reçus si nécessaire. - N'abusez pas de *args : si votre fonction attend toujours exactement deux ou trois arguments, utilisez des paramètres nommés explicites.
*argsdoit être réservé aux cas où le nombre d'arguments est réellement variable. - Préférez les annotations de type : depuis Python 3.11+, vous pouvez annoter
*argsavec*args: intpour indiquer le type attendu de chaque argument.
def moyenne(*args: float) -> float:
"""Calcule la moyenne d'un nombre variable de valeurs.
Args:
*args: Les valeurs numériques dont on souhaite
calculer la moyenne.
Returns:
La moyenne arithmétique des valeurs.
Raises:
ValueError: Si aucune valeur n'est fournie.
"""
if not args:
raise ValueError("Au moins une valeur est requise")
return sum(args) / len(args)
print(moyenne(10, 20, 30)) # 20.0Notez l'utilisation de la fonction len pour obtenir le nombre d'éléments du tuple args.
Cas d'utilisation courants de *args
Voici les situations les plus fréquentes où *args se révèle indispensable :
- Décorateurs : pour créer des décorateurs génériques qui fonctionnent avec n'importe quelle fonction.
- Fonctions mathématiques : somme, produit, moyenne, minimum, maximum sur un nombre variable de valeurs.
- Héritage de class : pour transmettre les arguments au constructeur de la classe parente avec
super().__init__(*args). - Wrappers et proxies : pour encapsuler un appel de fonction tout en transmettant fidèlement ses arguments.
- Fonctions d'affichage : la fonction print de Python elle-même utilise
*argspour accepter un nombre variable d'objets à afficher. - Concaténation de collections : fusionner plusieurs listes, tuples ou sets passés en arguments.
Erreurs courantes à éviter
Voici les pièges les plus fréquents liés à l'utilisation de *args :
Oublier l'astérisque lors de la décompression
def afficher(a, b, c):
print(a, b, c)
valeurs = (1, 2, 3)
# Erreur : passe le tuple entier comme premier argument
# afficher(valeurs) # TypeError
# Correct : décompresse le tuple
afficher(*valeurs) # 1 2 3Confondre *args (tuple) et une liste
def modifier(*args):
# args est un tuple, donc immuable
# args[0] = 10 # TypeError: 'tuple' does not support item assignment
# Convertir en liste si modification nécessaire
args_liste = list(args)
args_liste[0] = 10
return args_liste
print(modifier(1, 2, 3)) # [10, 2, 3]Placer *args après **kwargs
# ERREUR DE SYNTAXE :
# def mauvais(**kwargs, *args): # SyntaxError
# pass
# CORRECT :
def correct(*args, **kwargs):
passQuestions fréquentes
Est-ce que le nom "args" est obligatoire en Python ?
Non, le nom args n'est pas obligatoire. C'est l'astérisque * qui donne le comportement de collecte des arguments positionnels. Vous pourriez écrire *nombres, *elements ou tout autre nom valide. Cependant, *args est une convention très forte dans la communauté Python (définie dans la PEP 8), et la respecter rend votre code immédiatement compréhensible par les autres développeurs.
Peut-on utiliser *args avec des annotations de type ?
Oui, depuis Python 3.11 (PEP 646), vous pouvez annoter *args directement. Par exemple, def ma_fonction(*args: int) indique que tous les arguments collectés doivent être de type int. Avant Python 3.11, vous pouviez utiliser typing.Tuple ou typing.Any pour des annotations plus complexes. Ces annotations n'ont pas d'effet à l'exécution, mais elles sont exploitées par les outils d'analyse statique comme mypy.
Quelle est la différence entre *args dans une définition et dans un appel de fonction ?
Dans une définition de fonction (def f(*args)), l'opérateur * collecte (pack) les arguments positionnels dans un tuple. Dans un appel de fonction (f(*ma_liste)), l'opérateur * décompresse (unpack) un itérable en arguments individuels. Ce sont deux opérations inverses qui utilisent la même syntaxe.
Comment apprendre à maîtriser *args et les fonctions avancées en Python ?
Pour approfondir votre compréhension de *args, **kwargs, des décorateurs et de toutes les subtilités des fonctions en Python, nous vous recommandons de suivre notre formation Python sur Believemy. Vous y trouverez des exercices pratiques, des projets concrets et un accompagnement pédagogique complet pour maîtriser ces concepts essentiels du développement Python.