Question 1Que va afficher ce code ?def f(*args):
return sum(args)
print(f(1, 2, 3, 4))
Arguments à longueur variable *args / **kwargs et valeurs de retour multiples
Apprends args / *kwargs en Python pour accepter un nombre variable d'arguments et renvoyer plusieurs valeurs en un seul appel.
Dans l'article précédent sur def, tu as défini des fonctions avec un nombre fixe d'arguments. Dans le code réel, le nombre d'arguments n'est pas toujours connu d'avance, et parfois tu veux renvoyer plusieurs résultats en un seul appel.
Python gère cela avec les arguments à longueur variable et les valeurs de retour multiples. Les deux s'appuient directement sur ce que tu sais déjà des tuples et des dicts.
Arguments positionnels à longueur variable *args — collecter dans un tuple
Mets une seule astérisque devant le nom d'un argument (*args) et la fonction peut accepter n'importe quel nombre d'arguments positionnels. À l'intérieur de la fonction, ces arguments sont disponibles sous forme de tuple.
Le nom args est la convention, mais techniquement n'importe quel nom descriptif fonctionne — *prices, *messages, ce qui se lit bien pour le cas d'usage.
# Accepte n'importe quel nombre d'arguments positionnels
def sum_all(*prices):
print(prices) # (100, 200, 300)
print(type(prices)) # <class 'tuple'>
return sum(prices)
print(sum_all(100, 200, 300)) # 600
print(sum_all(500)) # 500 (un seul, ça marche)
print(sum_all()) # 0 (zéro donne un tuple vide)
Combiner des arguments classiques avec *args
Place les arguments classiques avant *args et tu peux combiner un argument fixe en tête avec une queue à longueur variable. Écrire log(level, *messages) veut dire que la première valeur est liée à level et que tout le reste est regroupé dans un tuple appelé messages. L'ordre est arguments classiques → *args.
def log(level, *messages):
for m in messages:
print(f"[{level}] {m}")
log("INFO", "Démarré", "Connecté")
# [INFO] Démarré
# [INFO] Connecté
Arguments nommés à longueur variable **kwargs — collecter dans un dict
Deux astérisques (`kwargs) permettent à la fonction d'accepter n'importe quels arguments nommés sous la forme name=value. À l'intérieur de la fonction, ces arguments arrivent sous forme de dict, ce qui rend cela parfait quand les clés ne sont pas connues à l'avance**.
Le nom conventionnel est kwargs (raccourci de keyword arguments).
Appeler show(name="Alice", age=30) donne au receveur le dict {"name": "Alice", "age": 30}. Ne pas avoir à déclarer les clés à l'avance, c'est tout l'intérêt.
def describe_user(**info):
print(info) # {'name': 'Alice', 'age': 30, 'city': 'Paris'}
print(type(info)) # <class 'dict'>
for key, value in info.items():
print(f"{key}: {value}")
describe_user(name="Alice", age=30, city="Paris")
Combiner des arguments classiques avec **kwargs
Place un argument classique avant **kwargs et tu obtiens **une valeur fixe obligatoire** plus **des champs optionnels flexibles**. Définir def save_user(name, **info): veut dire que name est toujours requis, tandis que des champs supplémentaires comme age=30 ou city="Paris" se font collecter dans le dict info.
def save_user(name, **info):
print(f"name: {name}")
for key, value in info.items():
print(f"{key}: {value}")
save_user("Alice", age=30, city="Paris")
# name: Alice
# age: 30
# city: Paris
Utiliser arguments classiques, *args et **kwargs ensemble
Les trois sortes d'arguments peuvent coexister dans une seule fonction. L'ordre est figé : arguments classiques → *args → **kwargs. Écrire def register(user_id, *tags, **meta): lie la première valeur à user_id, regroupe tout argument positionnel supplémentaire dans le tuple tags, et regroupe chaque argument key=value dans le dict meta.
def register(user_id, *tags, **meta):
print(f"id: {user_id}")
print(f"tags: {tags}")
print(f"meta: {meta}")
register(1001, "admin", "beta", email="alice@example.com", active=True)
# id: 1001
# tags: ('admin', 'beta')
# meta: {'email': 'alice@example.com', 'active': True}
Dépaquetage côté appelant — *list / **dict
Quand tu passes une liste ou un dict existants à une fonction, mettre ou non * ou ** devant change le résultat de manière radicale.
Si tu la passes telle quelle sans * ni **, la liste ou le dict est traité **comme une seule valeur qui ne se dépaquette pas**. Reçois-la via *args et tu finiras avec un tuple contenant une liste (par ex. ([1, 2, 3],)`) — c'est le piège.
Avec `list / dict côté appel, le contenu est étalé un par un, donc *args / **kwargs voient bien le tuple (1, 2, 3) ou le dict {"name": "Alice"} attendus.
def sum_all(*prices):
return sum(prices)
prices = [120, 280, 550]
# X : passé comme une seule liste
# print(sum_all(prices)) # erreur (sum essaie d'additionner la liste elle-même à des nombres)
# O : étale-la avec `*`
print(sum_all(*prices)) # 950 — pareil que sum_all(120, 280, 550)
# Même idée avec les dicts
def announce(name, city):
print(f"{name} de {city}")
user = {"name": "Léa", "city": "Marseille"}
announce(**user) # pareil que announce(name="Léa", city="Marseille")
Le dépaquetage brille quand on passe des données existantes
Quand tu as déjà des données sous forme de liste ou de dict, le dépaquetage les fait entrer dans une fonction en une seule ligne.
Par exemple, le dict user = {"name": "Léa", "city": "Marseille"} devient announce(**user) — c'est pareil qu'écrire announce(name="Léa", city="Marseille"). Pas besoin d'extraire chaque clé à la main.
Valeurs de retour multiples — renvoyer un tuple, dépaqueter à la réception
return accepte plusieurs valeurs séparées par des virgules, et l'appelant reçoit un tuple de ces valeurs. Reçois-les avec a, b = function() pour dépaqueter le tuple en plusieurs variables d'un coup.
C'est pratique chaque fois que tu veux renvoyer plusieurs valeurs depuis une fonction (min et max, nom d'utilisateur et rôle, drapeau de succès et message, etc.).
# Renvoyer plusieurs valeurs en une fois
def get_person():
return "Alice", 30, "Paris"
# Recevoir comme un tuple
result = get_person()
print(result) # ('Alice', 30, 'Paris')
print(type(result)) # <class 'tuple'>
# Ou dépaqueter en variables individuelles
name, age, city = get_person()
print(name, age, city) # Alice 30 Paris
# Exemple concret : renvoyer min et max ensemble
def get_min_max(values):
return min(values), max(values)
lowest, highest = get_min_max([120, 500, 300, 180])
print(f"min {lowest} / max {highest}")
Utilise *rest pour récupérer ce qui reste
Avec une liste de notes comme return 90, 85, 78, 92, 88, écris first, *middle, last = ... et tu obtiendras first=90, last=88, middle=[85, 78, 92] — les premières et dernières valeurs, et le reste rassemblé dans une liste. C'est l'outil parfait quand tu veux garder seulement le début et la fin et regrouper tout ce qui est entre les deux.
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Quel est le type de kwargs reçu par def f(**kwargs): ?
Question 3Avec nums = [1, 2, 3], quel appel passe son contenu comme trois arguments positionnels à f(a, b, c) ?