Skip to main content
Niveau : Intermédiaire

Chapitre 16 : Arguments Flexibles (*args et **kwargs)

*args, **kwargs, Déballage

1. Quoi : *args et **kwargs

*args et **kwargs sont des conventions de syntaxe en Python qui permettent à une fonction d'accepter un nombre variable d'arguments.

  • *args (Arguments) : Permet de passer un nombre variable d'arguments positionnels. Python les regroupe dans un tuple.
  • **kwargs (Keyword Arguments) : Permet de passer un nombre variable d'arguments nommés. Python les regroupe dans un dictionnaire.

Les noms args et kwargs sont une convention. C'est l'astérisque (*) et le double astérisque (**) qui sont importants.

2. Pourquoi : Créer des fonctions flexibles

Ces syntaxes sont utiles dans de nombreux scénarios :

  • Créer des fonctions qui peuvent opérer sur un nombre indéterminé d'entrées (ex: une fonction sum() qui additionne tous les nombres passés en argument).
  • Créer des "wrappers" ou des "décorateurs" qui doivent transmettre des arguments à une autre fonction sans connaître ces arguments à l'avance.
  • Étendre les fonctionnalités d'une fonction existante sans casser la compatibilité avec l'ancienne version.

3. Comment : Utilisation de *args

*args collecte tous les arguments positionnels supplémentaires dans un tuple.

# 'a' et 'b' sont des paramètres positionnels normaux.
# *args collecte tout le reste.
def my_function(a, b, *args):
print(f"a: {a}")
print(f"b: {b}")
print(f"args: {args}") # args est un tuple
print("-" * 10)

my_function(1, 2)
# a: 1
# b: 2
# args: () (un tuple vide)

my_function(1, 2, 3, 4, 5)
# a: 1
# b: 2
# args: (3, 4, 5)

Exemple pratique : une fonction sum_all

def sum_all(*numbers):
"""Calculates the sum of all numbers passed as arguments."""
total = 0
for number in numbers: # 'numbers' est un tuple
total += number
return total

print(sum_all(1, 2, 3)) # 6
print(sum_all(10, 20, 30, 40)) # 100
print(sum_all()) # 0

4. Comment : Utilisation de **kwargs

**kwargs collecte tous les arguments nommés supplémentaires dans un dictionnaire.

def display_info(**kwargs):
print(f"kwargs: {kwargs}") # kwargs est un dictionnaire
for key, value in kwargs.items():
print(f" - {key}: {value}")
print("-" * 10)

display_info(name="Alice", age=30)
# kwargs: {'name': 'Alice', 'age': 30}
# - name: Alice
# - age: 30

display_info(user_id=123, status="active", theme="dark")
# kwargs: {'user_id': 123, 'status': 'active', 'theme': 'dark'}
# - user_id: 123
# - status: active
# - theme: dark

5. Combinaison de tous les types d'arguments

L'ordre des paramètres dans la définition d'une fonction doit être respecté :

  1. Arguments positionnels standards.
  2. *args.
  3. Arguments nommés standards (après *args).
  4. **kwargs.
def kitchen_sink(a, b, *args, setting1="default", **kwargs):
print(f"a: {a}")
print(f"b: {b}")
print(f"args: {args}")
print(f"setting1: {setting1}")
print(f"kwargs: {kwargs}")
print("-" * 20)

kitchen_sink(1, 2, 3, 4, setting1="custom", user="Alice", score=99)
# a: 1
# b: 2
# args: (3, 4)
# setting1: custom
# kwargs: {'user': 'Alice', 'score': 99}

6. Déballage d'arguments (* et ** à l'appel)

On peut aussi utiliser * et ** lors de l'appel d'une fonction pour "déballer" une liste/tuple ou un dictionnaire en arguments.

Déballage avec *

Si vous avez une liste ou un tuple, vous pouvez le passer comme arguments positionnels à une fonction.

def add(a, b, c):
return a + b + c

my_list = [10, 20, 30]

# Au lieu de faire add(my_list[0], my_list[1], my_list[2])
# On peut "déballer" la liste :
result = add(*my_list)
print(result) # 60

Déballage avec **

Si vous avez un dictionnaire, vous pouvez le passer comme arguments nommés à une fonction. Les clés du dictionnaire doivent correspondre aux noms des paramètres de la fonction.

def create_user(name, age, city):
print(f"User: {name}, Age: {age}, City: {city}")

user_data = {
"name": "Bob",
"age": 42,
"city": "New York"
}

# Le dictionnaire est déballé en arguments nommés :
# name="Bob", age=42, city="New York"
create_user(**user_data)

Cette technique est extrêmement utile pour appeler des fonctions de manière dynamique à partir de données de configuration ou de résultats d'API.

Exercices :

Exercice 16 - Générateur de Balises HTML

Objectif

Cet exercice a pour but de vous faire utiliser *args et **kwargs pour créer une fonction flexible capable de générer des balises HTML.

Contexte

En HTML, une balise est composée d'un nom (ex: p, div, a) et peut avoir de multiples attributs (ex: class="main", id="intro", href="/"). Vous allez créer une fonction qui prend le nom de la balise, son contenu, et un nombre variable d'attributs pour générer la chaîne de caractères HTML correspondante.

Énoncé

  1. Créez un nouveau fichier Python nommé html_tag_generator.py.

  2. Définissez une fonction nommée create_html_tag qui accepte les paramètres suivants dans cet ordre :

    • tag_name : Le nom de la balise (ex: "p").
    • content : Le contenu textuel de la balise.
    • **attributes : Un dictionnaire pour capturer tous les attributs HTML nommés.
  3. À l'intérieur de la fonction : a. Commencez par construire la partie ouvrante de la balise. Par exemple, si tag_name est "p", la base est <p.

    b. Itérez sur les paires clé-valeur du dictionnaire attributes (**kwargs). Pour chaque attribut, ajoutez nom_attribut="valeur_attribut" à la chaîne de la balise. - Exemple : Si attributes est {'class': 'text-bold', 'id': 'p1'}, vous devez ajouter class="text-bold" id="p1". - N'oubliez pas l'espace avant chaque attribut.

    c. Fermez la balise ouvrante avec >.

    d. Ajoutez le content.

    e. Ajoutez la balise fermante, qui est </ + tag_name + >.

    f. Retournez la chaîne de caractères HTML complète.

  4. Testez votre fonction avec les appels suivants et affichez les résultats :

    • Une balise p simple : create_html_tag("p", "This is a paragraph.")
    • Une balise a avec des attributs : create_html_tag("a", "Click here", href="/home", target="_blank")
    • Une balise div avec une classe et un id : create_html_tag("div", "This is a section.", id="main-section", class_name="container")
      • Note : Comme class est un mot-clé réservé en Python, on utilise souvent class_name comme nom de paramètre et on le traite spécialement. Pour cet exercice, vous pouvez générer class_name="..." ou, en bonus, le convertir en class="...".

Résultat Attendu

<p>This is a paragraph.</p>
<a href="/home" target="_blank">Click here</a>
<div id="main-section" class_name="container">This is a section.</div>

Bonus

Modifiez votre fonction pour qu'elle gère correctement l'attribut class. Si la fonction reçoit un argument nommé class_name, elle doit le transformer en un attribut class dans la balise HTML.

Résultat Attendu (avec bonus)

<p>This is a paragraph.</p>
<a href="/home" target="_blank">Click here</a>
<div id="main-section" class="container">This is a section.</div>
Cliquez ici pour voir un exemple de code de solution
# html_tag_generator.py

def create_html_tag(tag_name, content, **attributes):
"""
Generates an HTML tag with content and dynamic attributes.

Args:
tag_name (str): The name of the HTML tag (e.g., 'p', 'div').
content (str): The text content inside the tag.
**attributes: Keyword arguments representing HTML attributes.
Use 'class_name' for the 'class' attribute.

Returns:
str: The complete HTML tag as a string.
"""
# Commence la balise ouvrante
tag_parts = [f"<{tag_name}"]

# Gère le cas spécial de 'class' (Bonus)
if 'class_name' in attributes:
attributes['class'] = attributes.pop('class_name')

# Ajoute les attributs
for key, value in attributes.items():
tag_parts.append(f' {key}="{value}"')

# Termine la balise
tag_parts.append(f">{content}</{tag_name}>")

# Joint toutes les parties en une seule chaîne
return "".join(tag_parts)

# --- Tests ---

# Test 1: Balise p simple
p_tag = create_html_tag("p", "This is a paragraph.")
print(p_tag)

# Test 2: Balise a avec attributs
a_tag = create_html_tag("a", "Click here", href="/home", target="_blank")
print(a_tag)

# Test 3: Balise div avec class_name (Bonus)
div_tag = create_html_tag("div", "This is a section.", id="main-section", class_name="container")
print(div_tag)