Les décorateurs permettent de modifier le comportement d'une fonction sans changer son code.
def mon_decorateur(func):
def wrapper():
print("Avant la fonction")
func()
print("Après la fonction")
return wrapper
@mon_decorateur
def dire_bonjour():
print("Bonjour!")
dire_bonjour()
# Sortie:
# Avant la fonction
# Bonjour!
# Après la fonctiondef decorateur_avec_args(func):
def wrapper(*args, **kwargs):
print(f"Arguments: {args}, {kwargs}")
resultat = func(*args, **kwargs)
print(f"Résultat: {resultat}")
return resultat
return wrapper
@decorateur_avec_args
def additionner(a, b):
return a + b
additionner(5, 3)def repeter(n):
def decorateur(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorateur
@repeter(3)
def saluer(nom):
print(f"Bonjour {nom}!")
saluer("Alice") # Affiche 3 foisimport time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
debut = time.time()
resultat = func(*args, **kwargs)
fin = time.time()
print(f"{func.__name__} a pris {fin - debut:.4f} secondes")
return resultat
return wrapper
@timer
def calcul_lent():
time.sleep(1)
return sum(range(1000000))
calcul_lent()class GestionFichier:
def __init__(self, nom_fichier, mode):
self.nom_fichier = nom_fichier
self.mode = mode
self.fichier = None
def __enter__(self):
self.fichier = open(self.nom_fichier, self.mode)
return self.fichier
def __exit__(self, exc_type, exc_val, exc_tb):
if self.fichier:
self.fichier.close()
return False
# Utilisation
with GestionFichier("test.txt", "w") as f:
f.write("Hello World!")
# Le fichier est automatiquement ferméfrom contextlib import contextmanager
@contextmanager
def timer_context():
import time
debut = time.time()
yield
fin = time.time()
print(f"Temps écoulé: {fin - debut:.4f}s")
# Utilisation
with timer_context():
# Code à chronométrer
sum(range(1000000))from functools import reduce
# Map: appliquer une fonction à chaque élément
nombres = [1, 2, 3, 4, 5]
doubles = list(map(lambda x: x * 2, nombres))
print(doubles) # [2, 4, 6, 8, 10]
# Filter: filtrer selon une condition
pairs = list(filter(lambda x: x % 2 == 0, nombres))
print(pairs) # [2, 4]
# Reduce: réduire à une seule valeur
somme = reduce(lambda x, y: x + y, nombres)
print(somme) # 15
# Combinaison
resultat = reduce(
lambda x, y: x + y,
map(lambda x: x ** 2,
filter(lambda x: x % 2 == 0, range(10)))
)
print(resultat) # 120 (0² + 2² + 4² + 6² + 8²)# Fonction qui retourne une fonction
def creer_multiplicateur(n):
def multiplicateur(x):
return x * n
return multiplicateur
fois_deux = creer_multiplicateur(2)
fois_trois = creer_multiplicateur(3)
print(fois_deux(5)) # 10
print(fois_trois(5)) # 15
# Fonction qui prend une fonction en argument
def appliquer_operation(operation, liste):
return [operation(x) for x in liste]
nombres = [1, 2, 3, 4, 5]
carres = appliquer_operation(lambda x: x**2, nombres)
print(carres) # [1, 4, 9, 16, 25]import re
texte = "Mon email est alice@example.com et mon numéro est 06-12-34-56-78"
# Chercher un pattern
email = re.search(r'[\w\.-]+@[\w\.-]+', texte)
if email:
print(f"Email trouvé: {email.group()}")
# Trouver toutes les occurrences
mots = re.findall(r'\b\w+\b', texte)
print(f"Mots: {mots}")
# Remplacer
cache = re.sub(r'\d{2}-\d{2}-\d{2}-\d{2}-\d{2}', 'XX-XX-XX-XX-XX', texte)
print(cache)
# Groupes de capture
pattern = r'(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2})'
match = re.search(pattern, texte)
if match:
print(f"Numéro complet: {match.group(0)}")
print(f"Premier groupe: {match.group(1)}")
# Compilation pour performances
pattern_compile = re.compile(r'\d+')
nombres = pattern_compile.findall("Il y a 123 pommes et 456 oranges")
print(nombres) # ['123', '456']import threading
import time
def tache(nom, duree):
print(f"{nom} démarre")
time.sleep(duree)
print(f"{nom} terminé")
# Créer des threads
threads = []
for i in range(5):
t = threading.Thread(target=tache, args=(f"Thread-{i}", 1))
threads.append(t)
t.start()
# Attendre tous les threads
for t in threads:
t.join()
print("Tous les threads sont terminés")import threading
class MonThread(threading.Thread):
def __init__(self, nom):
super().__init__()
self.nom = nom
def run(self):
print(f"{self.nom} démarre")
time.sleep(1)
print(f"{self.nom} terminé")
# Utilisation
threads = [MonThread(f"Thread-{i}") for i in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()import threading
compteur = 0
lock = threading.Lock()
def incrementer():
global compteur
for _ in range(100000):
with lock:
compteur += 1
threads = [threading.Thread(target=incrementer) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"Compteur: {compteur}")from multiprocessing import Process, Pool
import os
def travail(nom):
print(f"Process {nom} - PID: {os.getpid()}")
return nom * 2
# Avec Process
if __name__ == '__main__':
processes = []
for i in range(5):
p = Process(target=travail, args=(i,))
processes.append(p)
p.start()
for p in processes:
p.join()
# Avec Pool
with Pool(4) as pool:
resultats = pool.map(travail, range(10))
print(resultats)from typing import List, Dict, Tuple, Optional, Union
def saluer(nom: str) -> str:
return f"Bonjour {nom}"
def additionner(a: int, b: int) -> int:
return a + b
# Collections
def traiter_liste(nombres: List[int]) -> int:
return sum(nombres)
def get_info() -> Tuple[str, int]:
return "Alice", 25
# Optional (peut être None)
def chercher(nom: str) -> Optional[Dict[str, str]]:
database = {"Alice": "alice@example.com"}
return database.get(nom)
# Union (plusieurs types possibles)
def traiter(valeur: Union[int, str]) -> str:
if isinstance(valeur, int):
return str(valeur)
return valeur.upper()
# Type alias
Coordonnees = Tuple[float, float]
def distance(point: Coordonnees) -> float:
x, y = point
return (x**2 + y**2) ** 0.5from dataclasses import dataclass, field
from typing import List
@dataclass
class Personne:
nom: str
age: int
email: str = "non@fourni.com" # Valeur par défaut
amis: List[str] = field(default_factory=list)
def anniversaire(self):
self.age += 1
# Utilisation
alice = Personne("Alice", 25)
print(alice) # Personne(nom='Alice', age=25, email='non@fourni.com', amis=[])
alice.amis.append("Bob")
alice.anniversaire()
print(alice.age) # 26
# Avec post_init
@dataclass
class Rectangle:
largeur: float
hauteur: float
aire: float = field(init=False)
def __post_init__(self):
self.aire = self.largeur * self.hauteur
rect = Rectangle(10, 20)
print(rect.aire) # 200import asyncio
import aiohttp
# Fonction asynchrone simple
async def dire_apres(delai, message):
await asyncio.sleep(delai)
print(message)
return message
# Exécuter plusieurs coroutines
async def main():
tache1 = asyncio.create_task(dire_apres(1, "Bonjour"))
tache2 = asyncio.create_task(dire_apres(2, "Au revoir"))
resultat1 = await tache1
resultat2 = await tache2
# Lancer
asyncio.run(main())
# Requêtes HTTP asynchrones
async def fetch(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def fetch_multiple():
urls = [
'https://api.example.com/1',
'https://api.example.com/2',
'https://api.example.com/3'
]
tasks = [fetch(url) for url in urls]
resultats = await asyncio.gather(*tasks)
return resultats
# asyncio.run(fetch_multiple())