args et kwargs avec Python

Comprendre *args et **kwargs en Python

En Python, *args et **kwargs permettent de passer un nombre variable d’arguments à une fonction. Voici une explication détaillée :

*args

  • Utilisation : *args permet de recevoir un nombre variable d’arguments positionnels.
  • Fonctionnement : Les arguments passés après le paramètre formel marqué par *args sont collectés dans un tuple.

Exemple :

def addition(*args):
    total = 0
    for nombre in args:
        total += nombre
    return total

print(addition(1, 2, 3)) # Affiche 6
print(addition(10, 20)) # Affiche 30

Dans cet exemple, *args recueille tous les arguments positionnels (1, 2, 3 ou 10, 20) dans le tuple args.

**kwargs

  • Utilisation : **kwargs permet de recevoir un nombre variable d’arguments nommés (keyword arguments).
  • Fonctionnement : Les arguments passés après le paramètre formel marqué par **kwargs sont collectés dans un dictionnaire où les clés sont les noms des paramètres et les valeurs sont les valeurs correspondantes.

Exemple :

def afficher_info(**kwargs):
    for cle, valeur in kwargs.items():
        print(f"{cle} : {valeur}")

afficher_info(nom="Alice", age=30, ville="Paris")

Ici, **kwargs recueille les arguments nommés dans un dictionnaire :

  • "nom": "Alice"
  • "age": 30
  • "ville": "Paris"

Combinaison de *args et **kwargs

Vous pouvez utiliser les deux dans une même fonction pour gérer à la fois des arguments positionnels et nommés. Il est important de noter que *args doit apparaître avant **kwargs.

def fonction_mixte(*args, **kwargs):
    print("Arguments positionnels:", args)
    print("Arguments nommés:", kwargs)

fonction_mixte(1, 2, 3, a=4, b=5)

Ce code affichera :

  • Arguments positionnels: (1, 2, 3)
  • Arguments nommés: {'a': 4, 'b': 5}

Conclusion

  • Utilité : Ces mécanismes rendent vos fonctions plus flexibles en permettant d’accepter un nombre variable d’arguments.
  • Bonne pratique : Utilisez-les lorsque vous ne savez pas exactement combien d’arguments seront fournis ou pour créer des interfaces génériques.

Ainsi, *args et **kwargs sont des outils très puissants pour écrire des fonctions modulables et réutilisables en Python.

Exemple avancé d'utilisation de *args dans une fonction

Pour illustrer la puissance de *args dans un cas concret, prenons l'exemple d'un script qui effectue un traitement de texte sur plusieurs fichiers. Cette fonction remplace un mot par un autre dans tous les fichiers passés en argument.

Extrait adapté du tutoriel Python de Alexandre GALODE, disponible sur developpez.com :

# Avec *args
def traitement(a_remplacer, remplacant, *args):
    for idx, fichier in enumerate(args):
        content = ""
        with open(fichier, 'r') as fichier_only:
            for line in fichier_only.readlines():
                line = line.replace(a_remplacer, remplacant)
                content += line

        with open(fichier, 'w') as fichier_only:
            fichier_only.write(content)

if __name__ == '__main__':
    traitement('riri', 'fifi', 'fichier1.txt')
    traitement('toto', 'tata', 'fichier1.txt', 'fichier2.txt', 'fichiern.txt') # le nombre d'arguments est variable
    liste_fichiers=['fichier3.txt', 'fichier4.txt', 'fichiers5.txt']
    traitement('fifi', 'loulou', *liste_fichiers) # passage d'une liste grâce à l'opérateur *

Grâce à *args, la fonction traitement peut manipuler dynamiquement un nombre quelconque de fichiers texte, qu'ils soient passés directement ou via une liste. Cela rend le script extrêmement flexible et adapté à une grande variété de scénarios.

Utilisation de **kwargs pour initialiser dynamiquement un objet

Une autre application très pratique de **kwargs est l'initialisation dynamique d’un objet. Cela permet de créer des instances d’une classe en passant un nombre variable d’attributs lors de la construction.

Voici un exemple avec une classe User :

class User:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)

utilisateur = User(prenom="Patrick", nom="Smith", age=30)
print(utilisateur.prenom, utilisateur.nom, utilisateur.age)

utilisateur2 = User(prenom="Luigi", age=25, nationalite="Italien")
print(utilisateur2.prenom, utilisateur2.age, utilisateur2.nationalite)

Dans cet exemple :

  • Le constructeur __init__ de la classe User accepte n’importe quel nombre d’attributs nommés grâce à **kwargs.
  • La fonction setattr est utilisée pour créer dynamiquement chaque attribut de l’objet avec la clé et la valeur fournies.

Ce qui est affiché :

Patrick Smith 30
Luigi 25 Italien

Ce pattern est particulièrement utile dans les cas où les attributs d’un objet peuvent varier selon le contexte d’utilisation, comme lors de la manipulation de données JSON ou de formulaires dynamiques.

Combiner *args et **kwargs dans une fonction

Il est possible de combiner *args et **kwargs dans une même fonction pour gérer à la fois des arguments positionnels et des arguments nommés. Voici deux exemples illustrant cette flexibilité :

Exemple 1 : Affichage des deux types d’arguments

def example_function(*args, **kwargs):
    print("Positional arguments:", args)
    print("Keyword arguments:", kwargs)

example_function(1, 2, 3, a="apple", b="banana")

Output:

Positional arguments: (1, 2, 3)
Keyword arguments: {'a': 'apple', 'b': 'banana'}

Ici, la fonction reçoit :

  • Des arguments positionnels : collectés dans args sous forme de tuple.
  • Des arguments nommés : collectés dans kwargs sous forme de dictionnaire.

Exemple 2 : Personnalisation d’un message de salutation

def greet(name, *args, **kwargs):
    greeting = kwargs.get('greeting', 'Hello')
    print(f"{greeting}, {name}!")
    if args:
        print("Additional names:", ', '.join(args))

greet("Alice")
greet("Alice", "Bob", "Charlie", greeting="Hi")

Output:

Hello, Alice!

Hi, Alice!
Additional names: Bob, Charlie

Ce second exemple montre :

  • Comment récupérer une valeur spécifique dans kwargs à l’aide de .get(), avec une valeur par défaut si l’argument est absent.
  • Comment afficher les noms supplémentaires passés en positionnel après le premier argument obligatoire.

Grâce à cette combinaison, vous pouvez créer des fonctions extrêmement souples, capables de s’adapter à un grand nombre de cas d’utilisation.