No history yet

Maîtrise des fonctions

Au-delà des bases des fonctions

Vous savez déjà comment définir et appeler une fonction en Python. Maintenant, explorons comment rendre ces fonctions plus dynamiques et puissantes. Les fonctions en Python ne sont pas de simples blocs de code ; ce sont des objets à part entière, ce qui ouvre un monde de possibilités.

Commençons par une question courante : comment créer une fonction qui accepte un nombre variable d'arguments ? C'est là qu'interviennent *args et **kwargs.

Arguments flexibles avec *args et **kwargs

Imaginez que vous écrivez une fonction qui additionne des nombres. Vous pourriez la définir pour deux ou trois arguments, mais que faire si vous voulez en additionner dix ? Ou seulement un ? C'est là que *args est utile. Il permet à une fonction de recevoir un nombre quelconque d'arguments positionnels, qu'elle regroupe dans un tuple.

def additionner_tout(*nombres):
    total = 0
    for nombre in nombres: # nombres est un tuple
        total += nombre
    return total

print(additionner_tout(1, 5, 3))
# Sortie: 9

print(additionner_tout(10, 20, 30, 40))
# Sortie: 100

De même, vous permet de gérer un nombre arbitraire d'arguments nommés (keyword arguments). Python les rassemble dans un dictionnaire, où les noms des arguments deviennent les clés.

def afficher_profil_utilisateur(**infos):
    print("Profil utilisateur :")
    for cle, valeur in infos.items(): # infos est un dictionnaire
        print(f"- {cle.capitalize()}: {valeur}")

afficher_profil_utilisateur(nom="Alice", age=30, ville="Paris")
# Sortie:
# Profil utilisateur :
# - Nom: Alice
# - Age: 30
# - Ville: Paris

Règle de base : *args pour une liste d'arguments sans nom, **kwargs pour une liste d'arguments nommés. Vous pouvez les utiliser ensemble, mais *args doit toujours précéder **kwargs dans la définition de la fonction.

Portée et Closures

Comprendre où Python cherche une variable est crucial pour éviter les bugs. L'ordre est défini par la règle : Local, Enclosing (englobant), Global, et Built-in (intégré).

  1. Local : L'intérieur de la fonction actuelle.
  2. Enclosing : L'intérieur de toute fonction imbriquée (englobante).
  3. Global : Le niveau supérieur du module ou du script.
  4. Built-in : Les noms pré-définis en Python, comme print() ou len().

Cette règle mène à un concept puissant : les closures (fermetures). Une closure est une fonction qui se souvient de l'environnement dans lequel elle a été créée, même après que la fonction externe a terminé son exécution. Cela se produit lorsqu'une fonction imbriquée fait référence à une variable de sa fonction englobante (la portée "Enclosing").

def create_multiplier(n):
    # 'n' est dans la portée englobante (Enclosing)
    def multiplier(x):
        # 'x' est dans la portée locale (Local)
        return x * n # 'n' est capturé par la closure
    return multiplier

# La fonction create_multiplier a terminé, mais 'n' est mémorisé.
double = create_multiplier(2)
triple = create_multiplier(3)

print(double(10)) # Sortie: 20
print(triple(10)) # Sortie: 30

Ici, double et triple sont des closures. Chacune se souvient de la valeur de n avec laquelle elle a été créée.

Fonctions Lambda et Décorateurs

Parfois, vous avez besoin d'une petite fonction anonyme pour une tâche rapide, sans la formalité d'une instruction def. Entrez les expressions : de petites fonctions d'une seule ligne.

# Trier une liste de tuples par le deuxième élément
points = [(1, 5), (4, 2), (9, 8)]

# Utilisation d'une lambda comme clé de tri
points.sort(key=lambda p: p[1])

print(points)
# Sortie: [(4, 2), (1, 5), (9, 8)]

Enfin, les décorateurs sont une manière très pythonique de modifier ou d'améliorer des fonctions. Un décorateur est une fonction qui prend une autre fonction en argument, y ajoute une fonctionnalité, et retourne la fonction modifiée. C'est comme emballer une fonction dans une autre.

def mon_decorateur(func):
    def wrapper():
        print("Quelque chose se passe avant l'appel de la fonction.")
        func()
        print("Quelque chose se passe après l'appel de la fonction.")
    return wrapper

@mon_decorateur
def dis_bonjour():
    print("Bonjour !")

dis_bonjour()

# Sortie:
# Quelque chose se passe avant l'appel de la fonction.
# Bonjour !
# Quelque chose se passe après l'appel de la fonction.

La syntaxe @mon_decorateur est juste un sucre syntaxique pour dis_bonjour = mon_decorateur(dis_bonjour). Les décorateurs sont largement utilisés en Python pour la journalisation, la gestion des permissions, la mise en cache, et bien plus encore.

Maintenant que vous comprenez ces concepts avancés, testons vos connaissances.

Quiz Questions 1/6

Que fait **kwargs dans la définition d'une fonction Python ?

Quiz Questions 2/6

Selon la règle LEGB, quel est l'ordre de recherche des variables en Python ?

Maîtriser ces techniques vous permettra d'écrire un code Python non seulement fonctionnel, mais aussi élégant, modulaire et réutilisable.