Concepts fondamentaux
Les fonctions imbriquées et les fermetures constituent la base des décorateurs en Python. Une fermeture est une fonction définie à l'intérieur d'une autre fonction qui référence des variables de la portée externe.
Exemple de fermeture simple
def fonction_externe():
valeur = 42
def fonction_interne():
print(f"Valeur capturée : {valeur}")
return fonction_interne
# Création de la fermeture
fermeture = fonction_externe()
fermeture() # Affiche : Valeur capturée : 42
Transmission de paramètres via fermeture
def create_comparateur(a, b):
def comparer():
return max(a, b)
return comparer
comparaison = create_comparateur(15, 8)
resultat = comparaison()
print(resultat) # Affiche : 15
Utilisation avec le module requests
Application pratique des fermetures pour encapsuler des requêtes HTTP :
import requests
def create_fetcher(url_cible):
def fetch_data():
try:
reponse = requests.get(url_cible)
if reponse.status_code == 200:
print(f"Longueur du contenu : {len(reponse.text)}")
return reponse.text
except Exception as e:
print(f"Erreur : {e}")
return fetch_data
# Création de fonctions spécialisées
fetch_baidu = create_fetcher("https://www.baidu.com")
contenu = fetch_baidu()
Introduction aux décorateurs
Un décorateur est un outil permettant d'ajouter des fonctionnalités à une fonction existante sans modifier son code source.
Décorateur basique pour mesurer le temps d'exécution
import time
def mesure_temps(func):
def wrapper(*args, **kwargs):
debut = time.time()
resultat = func(*args, **kwargs)
fin = time.time()
print(f"Temps d'exécution : {fin - debut:.4f} secondes")
return resultat
return wrapper
@mesure_temps
def traitement_long():
time.sleep(2)
print("Traitement terminé")
traitement_long()
Syntaxe sucre des décorateurs
# Équivalent à : fonction_originale = decorateur(fonction_originale)
@decorateur
def fonction_originale():
pass
Implémentation du panier d'achat
Structure complète d'un système d'achat avec gestion utilisateur :
import os
# Données utilisateur
etat_utilisateur = {
'nom': None,
'mot_de_passe': None,
'solde': None
}
# Catalogue produits
catalogue = [
['Vélo électrique', 1200],
['Lait bio', 3.50],
['Chaussures sport', 89.90],
['Jambon blanc', 4.20]
]
# Panier d'achat
panier = {}
def inscription():
while True:
nom = input("Nom utilisateur : ").strip()
if os.path.exists(f"{nom}.txt"):
print("Utilisateur déjà existant")
continue
mot_de_passe = input("Mot de passe : ").strip()
confirmation = input("Confirmer mot de passe : ").strip()
if mot_de_passe == confirmation:
donnees = f"{nom},{mot_de_passe},1000"
with open(f"{nom}.txt", 'w', encoding='utf-8') as f:
f.write(donnees)
print("Inscription réussie !")
break
else:
print("Mots de passe différents")
from functools import wraps
def authentification_requise(func):
@wraps(func)
def wrapper(*args, **kwargs):
if etat_utilisateur['nom']:
return func(*args, **kwargs)
else:
print("Authentification nécessaire")
connexion()
return func(*args, **kwargs)
return wrapper
def connexion():
while True:
nom = input("Nom : ").strip()
if not os.path.exists(f"{nom}.txt"):
print("Utilisateur inexistant")
continue
with open(f"{nom}.txt", 'r', encoding='utf-8') as f:
donnees = f.read().strip()
nom_fichier, mdp_fichier, solde_fichier = donnees.split(',')
if nom == nom_fichier:
mot_de_passe = input("Mot de passe : ").strip()
if mot_de_passe == mdp_fichier:
etat_utilisateur.update({
'nom': nom,
'mot_de_passe': mot_de_passe,
'solde': float(solde_fichier)
})
print("Connexion réussie")
break
else:
print("Mot de passe incorrect")
else:
print("Nom d'utilisateur incorrect")
@authentification_requise
def magasiner():
total_achats = 0
while True:
print("\n=== Magasin ===")
for idx, (produit, prix) in enumerate(catalogue, 1):
print(f"{idx}. {produit} - {prix}€")
choix = input("Choisissez un produit (q pour quitter) : ").strip()
if choix == 'q':
if total_achats > 0 and input("Finaliser les achats ? (o/n) : ") == 'o':
etat_utilisateur['solde'] -= total_achats
with open(f"{etat_utilisateur['nom']}.txt", 'w') as f:
nouvelles_donnees = f"{etat_utilisateur['nom']},{etat_utilisateur['mot_de_passe']},{etat_utilisateur['solde']}"
f.write(nouvelles_donnees)
print("Achats effectués !")
break
if choix.isdigit():
index = int(choix) - 1
if 0 <= index < len(catalogue):
produit, prix = catalogue[index]
if etat_utilisateur['solde'] >= prix:
panier[produit] = panier.get(produit, 0) + 1
total_achats += prix
print(f"Ajouté au panier : {produit}")
else:
print("Solde insuffisant")
else:
print("Choix invalide")
# Mapping des fonctionnalités
fonctions = {
'1': inscription,
'2': connexion,
'3': magasiner
}
def interface_principale():
print("Bienvenue dans notre boutique !")
while True:
print("\n1. S'inscrire\n2. Se connecter\n3. Magasiner\nq. Quitter")
choix = input("Votre choix : ").strip()
if choix == 'q':
print("Au revoir !")
break
elif choix in fonctions:
fonctions[choix]()
else:
print("Option invalide")
if __name__ == "__main__":
interface_principale()
Techniques avancées
Utilisation de wraps pour préserver les métadonnnées
from functools import wraps
def decorateur_tracage(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Appel de {func.__name__}")
return func(*args, **kwargs)
return wrapper
@decorateur_tracage
def exemple_fonction():
"""Documentation de la fonction"""
return "Résultat"
print(exemple_fonction.__name__) # exemple_fonction
print(exemple_fonction.__doc__) # Documentation de la fonction
Décorateurs multiples
def decorateur_a(func):
def wrapper(*args, **kwargs):
print("Décorateur A - Entrée")
resultat = func(*args, **kwargs)
print("Décorateur A - Sortie")
return resultat
return wrapper
def decorateur_b(func):
def wrapper(*args, **kwargs):
print("Décorateur B - Entrée")
resultat = func(*args, **kwargs)
print("Décorateur B - Sortie")
return resultat
return wrapper
@decorateur_a
@decorateur_b
def fonction_test():
print("Exécution principale")
fonction_test()
# Ordre d'exécution :
# Décorateur A - Entrée
# Décorateur B - Entrée
# Exécution principale
# Décorateur B - Sortie
# Décorateur A - Sortie