Couleurs et psychologie : Créer une palette cohérente
Introduction La couleur est l'un des outils les plus puissants du design web. Elle influence…
Python est l’un des langages de programmation les plus accessibles et puissants pour débuter dans le développement. Sa syntaxe claire et ses structures de données intégrées en font un choix idéal pour apprendre la programmation tout en construisant des applications réelles.
Dans cet article, vous découvrirez les fondamentaux de Python 3.10+ avec des exemples concrets, des bonnes pratiques, et des projets pratiques pour consolider vos connaissances.
Avant de commencer, créons un environnement de développement isolé et professionnel.
# Vérifier la version de Python (3.10 ou supérieur requis)
python3 --version
# Créer un environnement virtuel
python3 -m venv venv
# Activer l'environnement virtuel
# Sur Linux/Mac:
source venv/bin/activate
# Sur Windows:
# venvScriptsactivate
# Mettre à jour pip
pip install --upgrade pip
# requirements.txt - Dépendances pour le développement
pytest==7.4.3
pytest-cov==4.1.0
black==23.12.1
mypy==1.7.1
pylint==3.0.3
ipython==8.18.1
Installation des dépendances:
pip install -r requirements.txt
"""
basictypes.py - Démonstration des types de base en Python
"""
from typing import Final
# Constantes (convention: MAJUSCULES)
MAXUSERS: Final[int] = 100
PI: Final[float] = 3.14159
APPNAME: Final[str] = "MonApplication"
# Variables avec type hints
age: int = 25
prix: float = 19.99
nom: str = "Alice"
estactif: bool = True
# Inférence de type (Python devine le type)
ville = "Paris" # Python sait que c'est une str
compteur = 0 # Python sait que c'est un int
def afficherinformations(nom: str, age: int, ville: str) -> None:
"""
Affiche les informations d'un utilisateur.
Args:
nom: Le nom de l'utilisateur
age: L'âge de l'utilisateur
ville: La ville de résidence
"""
print(f"Nom: {nom}")
print(f"Age: {age} ans")
print(f"Ville: {ville}")
print(f"Application: {APPNAME}")
if name == "main":
afficherinformations(nom, age, ville)
"""
operators.py - Opérateurs arithmétiques, logiques et de comparaison
"""
from typing import Union
def calculatricebasique(
a: Union[int, float],
b: Union[int, float],
operation: str
) -> Union[int, float]:
"""
Effectue une opération arithmétique de base.
Args:
a: Premier nombre
b: Deuxième nombre
operation: Type d'opération (+, -, , /, //, %, )
Returns:
Le résultat de l'opération
Raises:
ValueError: Si l'opération n'est pas reconnue
ZeroDivisionError: Si division par zéro
"""
operations = {
"+": a + b, # Addition
"-": a - b, # Soustraction
"": a b, # Multiplication
"/": a / b, # Division
"//": a // b, # Division entière
"%": a % b, # Modulo (reste)
"": a b, # Puissance
}
if operation not in operations:
raise ValueError(f"Opération '{operation}' non reconnue")
return operations[operation]
# Opérateurs de comparaison
def estdansintervalle(valeur: float, minval: float, maxval: float) -> bool:
"""Vérifie si une valeur est dans un intervalle."""
return minval <= valeur <= maxval
# Opérateurs logiques
def peutvoter(age: int, nationalite: str, casierjudiciaire: bool) -> bool:
"""
Détermine si une personne peut voter.
Args:
age: L'âge de la personne
nationalite: La nationalité
casierjudiciaire: True si casier judiciaire
Returns:
True si la personne peut voter
"""
return (
age >= 18
and nationalite == "française"
and not casierjudiciaire
)
if name == "main":
# Tests des opérateurs
print(f"10 + 5 = {calculatricebasique(10, 5, '+')}")
print(f"10 / 3 = {calculatricebasique(10, 3, '/')}")
print(f"10 // 3 = {calculatricebasique(10, 3, '//')}")
print(f"10 % 3 = {calculatricebasique(10, 3, '%')}")
print(f"2 8 = {calculatricebasique(2, 8, '')}")
print(f"n15 est entre 10 et 20: {estdansintervalle(15, 10, 20)}")
print(f"Peut voter (25 ans, française, pas de casier): {peutvoter(25, 'française', False)}")
"""
listsoperations.py - Opérations avancées sur les listes
"""
from typing import List, Optional, Any
def gestiontaches() -> None:
"""Démonstration complète des opérations sur les listes."""
# Création de listes
taches: List[str] = ["Apprendre Python", "Faire les courses", "Répondre aux emails"]
nombres: List[int] = [1, 2, 3, 4, 5]
mixte: List[Any] = [1, "texte", 3.14, True] # Type mixte (éviter en production)
# Ajout d'éléments
taches.append("Appeler le client") # Ajoute à la fin
taches.insert(0, "Vérifier les emails") # Insère à l'index 0
taches.extend(["Réunion d'équipe", "Code review"]) # Ajoute plusieurs éléments
# Suppression d'éléments
taches.remove("Faire les courses") # Supprime par valeur
elementsupprime = taches.pop() # Supprime et retourne le dernier élément
del taches[0] # Supprime par index
# Accès et modification
premieretache = taches[0]
dernieretache = taches[-1]
taches[1] = "Nouvelle tâche"
# Slicing (découpage)
deuxpremieres = taches[:2]
deuxdernieres = taches[-2:]
milieu = taches[1:3]
# List comprehension (compréhension de liste)
nombrescarres = [n 2 for n in nombres]
nombrespairs = [n for n in nombres if n % 2 == 0]
print("Tâches actuelles:", taches)
print("Carrés:", nombrescarres)
print("Nombres pairs:", nombrespairs)
def filtrerettransformer(
nombres: List[int],
seuil: int
) -> List[int]:
"""
Filtre et transforme une liste de nombres.
Args:
nombres: Liste de nombres à traiter
seuil: Valeur minimale pour le filtre
Returns:
Liste des nombres supérieurs au seuil, multipliés par 2
"""
return [n 2 for n in nombres if n > seuil]
def trouverelement(
liste: List[Any],
element: Any
) -> Optional[int]:
"""
Trouve l'index d'un élément dans une liste.
Args:
liste: La liste à parcourir
element: L'élément à chercher
Returns:
L'index de l'élément ou None si non trouvé
"""
try:
return liste.index(element)
except ValueError:
return None
if name == "main":
gestiontaches()
nombres = [1, 5, 10, 15, 20, 25]
resultat = filtrerettransformer(nombres, 10)
print(f"nNombres > 10, multipliés par 2: {resultat}")
index = trouverelement(nombres, 15)
print(f"Index de 15: {index}")
"""
dictionariesoperations.py - Gestion avancée des dictionnaires
"""
from typing import Dict, List, Optional, Any
def gestionutilisateurs() -> None:
"""Démonstration des opérations sur les dictionnaires."""
# Création de dictionnaires
utilisateur: Dict[str, Any] = {
"id": 1,
"nom": "Dupont",
"prenom": "Marie",
"age": 28,
"email": "marie.dupont@example.com",
"roles": ["user", "moderator"]
}
# Accès aux valeurs
nom = utilisateur["nom"]
age = utilisateur.get("age") # Plus sûr (retourne None si absent)
telephone = utilisateur.get("telephone", "Non renseigné") # Valeur par défaut
# Modification et ajout
utilisateur["age"] = 29
utilisateur["ville"] = "Paris"
# Suppression
del utilisateur["roles"]
emailsupprime = utilisateur.pop("email")
# Itération
for cle, valeur in utilisateur.items():
print(f"{cle}: {valeur}")
# Clés et valeurs
touteslescles = list(utilisateur.keys())
touteslesvaleurs = list(utilisateur.values())
print(f"nClés: {touteslescles}")
print(f"Valeurs: {touteslesvaleurs}")
class GestionnaireUtilisateurs:
"""Gestionnaire d'utilisateurs avec dictionnaire."""
def init(self) -> None:
"""Initialise le gestionnaire."""
self.utilisateurs: Dict[int, Dict[str, Any]] = {}
self.prochainid: int = 1
def ajouterutilisateur(
self,
nom: str,
prenom: str,
email: str,
age: int
) -> int:
"""
Ajoute un nouvel utilisateur.
Args:
nom: Nom de famille
prenom: Prénom
email: Adresse email
age: Age
Returns:
L'ID de l'utilisateur créé
"""
utilisateurid = self.prochainid
self.utilisateurs[utilisateurid] = {
"nom": nom,
"prenom": prenom,
"email": email,
"age": age
}
self.prochainid += 1
return utilisateurid
def obtenirutilisateur(self, userid: int) -> Optional[Dict[str, Any]]:
"""Récupère un utilisateur par son ID."""
return self.utilisateurs.get(userid)
def rechercherparage(self, agemin: int, agemax: int) -> List[Dict[str, Any]]:
"""
Recherche des utilisateurs par tranche d'âge.
Args:
agemin: Age minimum
agemax: Age maximum
Returns:
Liste des utilisateurs correspondants
"""
return [
{user, "id": uid}
for uid, user in self.utilisateurs.items()
if agemin <= user["age"] <= agemax
]
def statistiques(self) -> Dict[str, Any]:
"""Calcule des statistiques sur les utilisateurs."""
if not self.utilisateurs:
return {"total": 0, "agemoyen": 0}
ages = [user["age"] for user in self.utilisateurs.values()]
return {
"total": len(self.utilisateurs),
"agemoyen": sum(ages) / len(ages),
"agemin": min(ages),
"agemax": max(ages)
}
if name == "main":
gestionutilisateurs()
print("n" + "="50)
print("Gestionnaire d'utilisateurs")
print("="50 + "n")
gestionnaire = GestionnaireUtilisateurs()
# Ajout d'utilisateurs
id1 = gestionnaire.ajouterutilisateur("Dupont", "Marie", "marie@example.com", 28)
id2 = gestionnaire.ajouterutilisateur("Martin", "Pierre", "pierre@example.com", 35)
id3 = gestionnaire.ajouterutilisateur("Durand", "Sophie", "sophie@example.com", 22)
# Recherche
jeunes = gestionnaire.rechercherparage(20, 30)
print(f"Utilisateurs entre 20 et 30 ans: {jeunes}")
# Statistiques
stats = gestionnaire.statistiques()
print(f"nStatistiques: {stats}")
"""
tuplessets.py - Tuples et ensembles
"""
from typing import Tuple, Set, FrozenSet, List
# TUPLES - Immuables, ordonnés
def coordonneesgps() -> Tuple[float, float]:
"""
Retourne des coordonnées GPS (latitude, longitude).
Returns:
Un tuple de coordonnées
"""
return 48.8566, 2.3522 # Paris
def informationspersonne() -> Tuple[str, int, str]:
"""Retourne des informations immuables sur une personne."""
return "Dupont", 28, "Paris"
# Named tuples pour plus de lisibilité
from typing import NamedTuple
class Coordonnees(NamedTuple):
"""Coordonnées GPS avec noms explicites."""
latitude: float
longitude: float
altitude: float = 0.0 # Valeur par défaut
class Personne(NamedTuple):
"""Informations d'une personne."""
nom: str
age: int
ville: str
email: str = ""
# SETS - Ensembles sans doublons
class GestionEnsembles:
"""Gestion d'ensembles pour opérations mathématiques."""
@staticmethod
def supprimerdoublons(elements: List[str]) -> List[str]:
"""
Supprime les doublons d'une liste en préservant l'ordre.
Args:
elements: Liste avec potentiellement des doublons
Returns:
Liste sans doublons
"""
# Méthode 1: avec set (ne préserve pas l'ordre)
# return list(set(elements))
# Méthode 2: préserve l'ordre
vu: Set[str] = set()
resultat: List[str] = []
for element in elements:
if element not in vu:
vu.add(element)
resultat.append(element)
return resultat
@staticmethod
def operationsensembles() -> None:
"""Démonstration des opérations sur les ensembles."""
# Création
fruits1: Set[str] = {"pomme", "banane", "orange"}
fruits2: Set[str] = {"banane", "kiwi", "mangue"}
# Union (tous les éléments)
tousfruits = fruits1 | fruits2
# ou: tousfruits = fruits1.union(fruits2)
# Intersection (éléments communs)
fruitscommuns = fruits1 & fruits2
# ou: fruitscommuns = fruits1.intersection(fruits2)
# Différence (dans fruits1 mais pas dans fruits2)
uniquementfruits1 = fruits1 - fruits2
# ou: uniquementfruits1 = fruits1.difference(fruits2)
# Différence symétrique (dans l'un ou l'autre, mais pas les deux)
fruitsdifferents = fruits1 ^ fruits2
# ou: fruitsdifferents = fruits1.symmetricdifference(fruits2)
print("Fruits1:", fruits1)
print("Fruits2:", fruits2)
print("Union:", tousfruits)
print("Intersection:", fruitscommuns)
print("Différence:", uniquementfruits1)
print("Différence symétrique:", fruitsdifferents)
@staticmethod
def tagsarticles() -> None:
"""Exemple pratique: gestion de tags d'articles."""
article1tags: Set[str] = {"python", "programmation", "debutant"}
article2tags: Set[str] = {"python", "web", "django"}
article3tags: Set[str] = {"javascript", "web", "react"}
# Trouver les tags communs à tous les articles Python
tagspython = article1tags & article2tags
print(f"nTags communs aux articles Python: {tagspython}")
# Tous les tags uniques
toustags = article1tags | article2tags | article3tags
print(f"Tous les tags: {toustags}")
# Tags spécifiques à Python
tagsspecifiquespython = (article1tags | article2tags) - article3tags
print(f"Tags spécifiques Python: {tagsspecifiquespython}")
# FrozenSet - Ensemble immuable (peut être clé de dictionnaire)
def creerpermissions() -> FrozenSet[str]:
"""Crée un ensemble immuable de permissions."""
return frozenset(["lire", "ecrire", "executer"])
if name == "main":
# Tuples
lat, lon = coordonneesgps()
print(f"Coordonnées Paris: {lat}, {lon}")
# Named tuples
paris = Coordonnees(48.8566, 2.3522, 35.0)
print(f"Paris - Latitude: {paris.latitude}, Longitude: {paris.longitude}")
personne = Personne("Dupont", 28, "Paris", "dupont@example.com")
print(f"Personne: {personne.nom}, {personne.age} ans")
# Sets
print("n" + "="50)
listeavecdoublons = ["a", "b", "c", "a", "b", "d"]
sansdoublons = GestionEnsembles.supprimerdoublons(listeavecdoublons)
print(f"Liste originale: {listeavecdoublons}")
print(f"Sans doublons: {sansdoublons}")
print("n" + "="50)
GestionEnsembles.operationsensembles()
GestionEnsembles.tagsarticles()
# FrozenSet
perms = creerpermissions()
print(f"nPermissions: {perms}")
"""
controlstructures.py - Structures de contrôle avancées
"""
from typing import List, Dict, Optional, Any
from enum import Enum
class NiveauUtilisateur(Enum):
"""Énumération des niveaux d'utilisateur."""
INVITE = "invite"
UTILISATEUR = "utilisateur"
MODERATEUR = "moderateur"
ADMINISTRATEUR = "administrateur"
def verifieracces(
niveau: NiveauUtilisateur,
age: int,
estverifie: bool
) -> str:
"""
Vérifie les droits d'accès d'un utilisateur.
Args:
niveau: Niveau de l'utilisateur
age: Age de l'utilisateur
estverifie: Si le compte est vérifié
Returns:
Message de statut d'accès
"""
# Conditions multiples avec elif
if niveau == NiveauUtilisateur.ADMINISTRATEUR:
return "Accès complet accordé"
elif niveau == NiveauUtilisateur.MODERATEUR and estverifie:
return "Accès modérateur accordé"
elif niveau == NiveauUtilisateur.UTILISATEUR and age >= 18 and estverifie:
return "Accès utilisateur accordé"
elif niveau == NiveauUtilisateur.UTILISATEUR and age < 18:
return "Accès utilisateur limité (mineur)"
elif not estverifie:
return "Vérification du compte requise"
else:
return "Accès invité limité"
def calculerremise(montant: float, codeclient: str) -> float:
"""
Calcule une remise selon le type de client.
Args:
montant: Montant de l'achat
codeclient: Code du type de client
Returns:
Montant de la remise
"""
# Pattern matching (Python 3.10+)
match codeclient:
case "VIP":
return montant 0.20
case "PREMIUM":
return montant 0.15
case "STANDARD":
return montant 0.10
case "NOUVEAU":
return montant 0.05 if montant > 100 else 0
case : # Cas par défaut
return 0
def traitercommandes(commandes: List[Dict[str, Any]]) -> None:
"""
Traite une liste de commandes avec différentes boucles.
Args:
commandes: Liste des commandes à traiter
"""
print("=== Traitement des commandes ===n")
# Boucle for classique
for i, commande in enumerate(commandes, start=1):
print(f"Commande #{i}:")
print(f" Client: {commande['client']}")
print(f" Montant: {commande['montant']}€")
# Continue: passe à l'itération suivante
if commande['montant'] < 10:
print(" -> Montant trop faible, ignorée")
continue
# Break: sort de la boucle
if commande.get('annulee', False):
print(" -> Commande annulée, arrêt du traitement")
break
print(" -> Traitée avec succès")
print("n=== Analyse des montants ===n")
# Boucle while
total = 0
index = 0
while index < len(commandes) and not commandes[index].get('annulee', False):
total += commandes[index]['montant']
index += 1
print(f"Total des commandes traitées: {total}€")
print(f"Nombre de commandes: {index}")
def filtrerettransformerdonnees(
donnees: List[int],
seuil: int
) -> Dict[str, List[int]]:
"""
Filtre et catégorise des données.
Args:
donnees: Liste de nombres à traiter
seuil: Valeur de référence
Returns:
Dictionnaire avec les données catégorisées
"""
petits: List[int] = []
moyens: List[int] = []
grands: List[int] = []
for valeur in donnees:
if valeur < seuil:
petits.append(valeur)
elif valeur < seuil 2:
moyens.append(valeur)
else:
grands.append(valeur)
return {
"petits": petits,
"moyens": moyens,
"grands": grands
}
# Compréhensions avancées
def comprehensionsavancees() -> None:
"""Démonstration de compréhensions avancées."""
# List comprehension avec condition
nombres = list(range(1, 21))
pairs = [n for n in nombres if n % 2 == 0]
carresimpairs = [n2 for n in nombres if n % 2 != 0]
# Dict comprehension
carresdict = {n: n2 for n in range(1, 11)}
pairsdict = {n: n2 for n in range(1, 11) if n % 2 == 0}
# Set comprehension
chiffresuniques = {n % 10 for n in range(100)}
# Nested comprehension (compréhension imbriquée)
matrice = [[ij for j in range(1, 6)] for i in range(1, 6)]
# List comprehension avec if-else
categories = ["pair" if n % 2 == 0 else "impair" for n in range(1, 11)]
print("Nombres pairs:", pairs)
print("Carrés des impairs:", carresimpairs)
print("Dictionnaire carrés:", carresdict)
print("Table de multiplication (5x5):")
for ligne in matrice:
print(ligne)
print("Catégories:", categories)
if name == "main":
# Test vérification d'accès
print(verifieracces(NiveauUtilisateur.UTILISATEUR, 25, True))
print(verifieracces(NiveauUtilisateur.UTILISATEUR, 16, True))
print(verifieracces(NiveauUtilisateur.MODERATEUR, 30, False))
print("n" + "="50 + "n")
# Test calcul de remise
print(f"Remise VIP sur 200€: {calculerremise(200, 'VIP')}€")
print(f"Remise NOUVEAU sur 50€: {calculerremise(50, 'NOUVEAU')}€")
print(f"Remise NOUVEAU sur 150€: {calculerremise(150, 'NOUVEAU')}€")
print("n" + "="50 + "n")
# Test traitement de commandes
commandes = [
{"client": "Alice", "montant": 150.00},
{"client": "Bob", "montant": 5.00},
{"client": "Charlie", "montant": 75.50},
{"client": "David", "montant": 200.00, "annulee": True},
{"client": "Eve", "montant": 50.00},
]
traitercommandes(commandes)
print("n" + "="50 + "n")
# Test filtrage de données
donnees = [5, 15, 25, 35, 45, 55, 8, 12, 18]
resultat = filtrerettransformerdonnees(donnees, 20)
print("Données filtrées:")
for categorie, valeurs in resultat.items():
print(f" {categorie}: {valeurs}")
print("n" + "="50 + "n")
comprehensionsavancees()
"""
functionsscope.py - Fonctions avancées et portée des variables
"""
from typing import List, Optional, Callable, Any
from functools import wraps
import time
# Variables globales (à éviter en production)
TAUXTVA: float = 0.20
def calculerprixttc(prixht: float, taux: Optional[float] = None) -> float:
"""
Calcule le prix TTC.
Args:
prixht: Prix hors taxes
taux: Taux de TVA personnalisé (utilise TAUXTVA si None)
Returns:
Prix toutes taxes comprises
"""
tauxutilise = taux if taux is not None else TAUXTVA
return prixht (1 + tauxutilise)
# Arguments par défaut, args et kwargs
def creerutilisateur(
nom: str,
prenom: str,
age: int,
email: Optional[str] = None,
roles: str,
metadata: Any
) -> dict[str, Any]:
"""
Crée un utilisateur avec paramètres flexibles.
Args:
nom: Nom de famille (requis)
prenom: Prénom (requis)
age: Age (requis)
email: Adresse email (optionnel)
roles: Rôles variables de l'utilisateur
metadata: Métadonnées additionnelles
Returns:
Dictionnaire représentant l'utilisateur
"""
utilisateur = {
"nom": nom,
"prenom": prenom,
"age": age,
"email": email or f"{prenom.lower()}.{nom.lower()}@example.com",
"roles": list(roles) if roles else ["user"],
"metadata": metadata
}
return utilisateur
# Fonctions de première classe (first-class functions)
def appliqueroperation(
nombres: List[float],
operation: Callable[[float], float]
) -> List[float]:
"""
Applique une opération à chaque nombre.
Args:
nombres: Liste de nombres
operation: Fonction à appliquer
Returns:
Liste des résultats
"""
return [operation(n) for n in nombres]
# Lambda functions
doubler = lambda x: x 2
carre = lambda x: x 2
estpair = lambda x: x % 2 == 0
# Closures (fermetures)
def creermultiplicateur(facteur: int) -> Callable[[int], int]:
"""
Crée une fonction de multiplication.
Args:
facteur: Le facteur de multiplication
Returns:
Fonction qui multiplie par le facteur
"""
def multiplicateur(x: int) -> int:
return x facteur
return multiplicateur
# Décorateurs
def chronometrer(func: Callable) -> Callable:
"""
Décorateur pour mesurer le temps d'exécution.
Args:
func: Fonction à chronométrer
Returns:
Fonction décorée
"""
@wraps(func)
def wrapper(args: Any, kwargs: Any) -> Any:
debut = time.time()
resultat = func(args, kwargs)
fin = time.time()
print(f"{func.name} a pris {fin - debut:.4f} secondes")
return resultat
return wrapper
def validerarguments(validations: Callable) -> Callable:
"""
Décorateur pour valider les arguments d'une fonction.
Args:
validations: Dictionnaire {nomarg: fonctionvalidation}
Returns:
Décorateur
"""
def decorateur(func: Callable) -> Callable:
@wraps(func)
def wrapper(args: Any, kwargs: Any) -> Any:
# Récupération des noms d'arguments
import inspect
sig = inspect.signature(func)
boundargs = sig.bind(args, kwargs)
boundargs.applydefaults()
# Validation
for nomarg, validateur in validations.items():
if nomarg in boundargs.arguments:
valeur = boundargs.arguments[nomarg]
if not validateur(valeur):
raise ValueError(
f"Validation échouée pour {nomarg}={valeur}"
)
return func(args, kwargs)
return wrapper
return decorateur
# Générateurs
def genererfibonacci(n: int):
"""
Génère les n premiers nombres de Fibonacci.
Args:
n: Nombre d'éléments à générer
Yields:
Nombres de Fibonacci
"""
a, b = 0, 1
for in range(n):
yield a
a, b = b, a + b
def lirefichierparblocs(
fichier: str,
taillebloc: int = 1024
):
"""
Lit un fichier par blocs (économie de mémoire).
Args:
fichier: Chemin du fichier
taillebloc: Taille de chaque bloc
Yields:
Blocs de données
"""
with open(fichier, 'r', encoding='utf-8') as f:
while True:
bloc = f.read(taillebloc)
if not bloc:
break
yield bloc
# Application pratique
@chronometrer
@validerarguments(
montant=lambda x: x > 0,
reduction=lambda x: 0 <= x <= 1
)
def calculerprixfinal(
montant: float,
reduction: float = 0.0,
codepromo: Optional[str] = None
) -> float:
"""
Calcule le prix final avec réductions.
Args:
montant: Montant initial
reduction: Taux de réduction (0 à 1)
codepromo: Code promotionnel optionnel
Returns:
Prix final
"""
prixreduit = montant (1 - reduction)
# Application du code promo
if codepromo == "BIENVENUE":
prixreduit = 0.90
elif codepromo == "VIP":
prixreduit = 0.85
return calculerprixttc(prixreduit)
if name == "main":
# Test création utilisateur
user1 = creerutilisateur(
"Dupont",
"Marie",
28,
roles=["user", "moderator"],
ville="Paris",
pays="France"
)
print("Utilisateur créé:", user1)
print("n" + "="50 + "n")
# Test fonctions de première classe
nombres = [1, 2, 3, 4, 5]
doubles = appliqueroperation(nombres, doubler)
carres = appliqueroperation(nombres, carre)
print(f"Nombres: {nombres}")
print(f"Doublés: {doubles}")
print(f"Carrés: {carres}")
print("n" + "="50 + "n")
# Test closures
multiplierpar3 = creermultiplicateur(3)
multiplierpar10 = creermultiplicateur(10)
print(f"5 3 = {multiplierpar3(5)}")
print(f"5 10 = {multiplierpar10(5)}")
print("n" + "="50 + "n")
# Test fonction décorée
prixfinal = calculerprixfinal(100.0, 0.15, "BIENVENUE")
print(f"Prix final: {prixfinal:.2f}€")
print("n" + "="50 + "n")
# Test générateur
print("10 premiers nombres de Fibonacci:")
for nombre in genererfibonacci(10):
print(nombre, end=" ")
print()
"""
taskmanager.py - Gestionnaire de tâches complet
"""
from typing import List, Optional, Dict, Any
from datetime import datetime, timedelta
from enum import Enum
import json
class Priorite(Enum):
"""Niveaux de priorité des tâches."""
BASSE = 1
MOYENNE = 2
HAUTE = 3
URGENTE = 4
class StatutTache(Enum):
"""Statuts possibles d'une tâche."""
AFAIRE = "à faire"
ENCOURS = "en cours"
TERMINEE = "terminée"
ANNULEE = "annulée"
class Tache:
"""Représente une tâche individuelle."""
def init(
self,
titre: str,
description: str = "",
priorite: Priorite = Priorite.MOYENNE,
echeance: Optional[datetime] = None
) -> None:
"""
Initialise une tâche.
Args:
titre: Titre de la tâche
description: Description détaillée
priorite: Niveau de priorité
echeance: Date limite
"""
self.id: int = 0 # Sera assigné par le gestionnaire
self.titre = titre
self.description = description
self.priorite = priorite
self.statut = StatutTache.AFAIRE
self.datecreation = datetime.now()
self.echeance = echeance
self.tags: List[str] = []
def estenretard(self) -> bool:
"""Vérifie si la tâche est en retard."""
if not self.echeance or self.statut == StatutTache.TERMINEE:
return False
return datetime.now() > self.echeance
def joursrestants(self) -> Optional[int]:
"""Calcule le nombre de jours restants."""
if not self.echeance:
return None
delta = self.echeance - datetime.now()
return delta.days
def todict(self) -> Dict[str, Any]:
"""Convertit la tâche en dictionnaire."""
return {
"id": self.id,
"titre": self.titre,
"description": self.description,
"priorite": self.priorite.name,
"statut": self.statut.value,
"datecreation": self.datecreation.isoformat(),
"echeance": self.echeance.isoformat() if self.echeance else None,
"tags": self.tags
}
def str(self) -> str:
"""Représentation textuelle de la tâche."""
retard = " [EN RETARD]" if self.estenretard() else ""
jours = self.joursrestants()
echeancestr = f" (J-{jours})" if jours is not None else ""
return (
f"[{self.id}] {self.titre} - "
f"{self.statut.value} - "
f"Priorité: {self.priorite.name}"
f"{echeancestr}{retard}"
)
class GestionnaireTaches:
"""Gestionnaire principal des tâches."""
def init(self) -> None:
"""Initialise le gestionnaire."""
self.taches: Dict[int, Tache] = {}
self.prochainid: int = 1
def ajoutertache(
self,
titre: str,
description: str = "",
priorite: Priorite = Priorite.MOYENNE,
echeance: Optional[datetime] = None,
tags: Optional[List[str]] = None
) -> Tache:
"""
Ajoute une nouvelle tâche.
Args:
titre: Titre de la tâche
description: Description
priorite: Niveau de priorité
echeance: Date limite
tags: Liste de tags
Returns:
La tâche créée
"""
tache = Tache(titre, description, priorite, echeance)
tache.id = self.prochainid
if tags:
tache.tags = tags
self.taches[self.prochainid] = tache
self.prochainid += 1
return tache
def obtenirtache(self, tacheid: int) -> Optional[Tache]:
"""Récupère une tâche par son ID."""
return self.taches.get(tacheid)
def supprimertache(self, tacheid: int) -> bool:
"""
Supprime une tâche.
Args:
tacheid: ID de la tâche à supprimer
Returns:
True si supprimée, False sinon
"""
if tacheid in self.taches:
del self.taches[tacheid]
return True
return False
def modifierstatut(
self,
tacheid: int,
nouveaustatut: StatutTache
) -> bool:
"""Modifie le statut d'une tâche."""
tache = self.obtenirtache(tacheid)
if tache:
tache.statut = nouveaustatut
return True
return False
def listertaches(
self,
statut: Optional[StatutTache] = None,
priorite: Optional[Priorite] = None,
tag: Optional[str] = None
) -> List[Tache]:
"""
Liste les tâches avec filtres optionnels.
Args:
statut: Filtrer par statut
priorite: Filtrer par priorité
tag: Filtrer par tag
Returns:
Liste des tâches correspondantes
"""
resultats = list(self.taches.values())
if statut:
resultats = [t for t in resultats if t.statut == statut]
if priorite:
resultats = [t for t in resultats if t.priorite == priorite]
if tag:
resultats = [t for t in resultats if tag in t.tags]
# Tri par priorité décroissante, puis par échéance
resultats.sort(
key=lambda t: (
-t.priorite.value,
t.echeance if t.echeance else datetime.max
)
)
return resultats
def tachesenretard(self) -> List[Tache]:
"""Retourne les tâches en retard."""
return [t for t in self.taches.values() if t.estenretard()]
def tachesurgentes(self, jours: int = 3) -> List[Tache]:
"""
Retourne les tâches urgentes (échéance proche).
Args:
jours: Nombre de jours pour considérer comme urgent
Returns:
Liste des tâches urgentes
"""
resultats = []
for tache in self.taches.values():
joursrestants = tache.joursrestants()
if (
joursrestants is not None
and 0 <= joursrestants <= jours
and tache.statut != StatutTache.TERMINEE
):
resultats.append(tache)
return resultats
def statistiques(self) -> Dict[str, Any]:
"""Calcule des statistiques sur les tâches."""
total = len(self.taches)
if total == 0:
return {"total": 0}
statsstatut = {}
for statut in StatutTache:
count = len([t for t in self.taches.values() if t.statut == statut])
statsstatut[statut.value] = count
statspriorite = {}
for priorite in Priorite:
count = len([t for t in self.taches.values() if t.priorite == priorite])
statspriorite[priorite.name] = count
return {
"total": total,
"parstatut": statsstatut,
"parpriorite": statspriorite,
"enretard": len(self.tachesenretard()),
"urgentes": len(self.tachesurgentes())
}
def sauvegarder(self, fichier: str) -> None:
"""
Sauvegarde les tâches dans un fichier JSON.
Args:
fichier: Chemin du fichier de sauvegarde
"""
donnees = {
"prochainid": self.prochainid,
"taches": [t.todict() for t in self.taches.values()]
}
with open(fichier, 'w', encoding='utf-8') as f:
json.dump(donnees, f, indent=2, ensureascii=False)
def charger(self, fichier: str) -> None:
"""
Charge les tâches depuis un fichier JSON.
Args:
fichier: Chemin du fichier à charger
"""
try:
with open(fichier, 'r', encoding='utf-8') as f:
donnees = json.load(f)
self.prochainid = donnees["prochainid"]
self.taches = {}
for tdict in donnees["taches"]:
tache = Tache(
titre=tdict["titre"],
description=tdict["description"],
priorite=Priorite[tdict["priorite"]]
)
tache.id = tdict["id"]
tache.statut = StatutTache(tdict["statut"])
tache.datecreation = datetime.fromisoformat(tdict["datecreation"])
if tdict["echeance"]:
tache.echeance = datetime.fromisoformat(tdict["echeance"])
tache.tags = tdict["tags"]
self.taches[tache.id] = tache
except FileNotFoundError:
print(f"Fichier {fichier} non trouvé, démarrage avec une liste vide")
def demogestionnaire() -> None:
"""Démonstration du gestionnaire de tâches."""
# Création du gestionnaire
gestionnaire = GestionnaireTaches()
# Ajout de tâches
print("=== Ajout de tâches ===n")
t1 = gestionnaire.ajoutertache(
"Apprendre Python",
"Suivre un cours complet sur Python",
Priorite.HAUTE,
datetime.now() + timedelta(days=7),
["formation", "python"]
)
print(f"Tâche ajoutée: {t1}")
t2 = gestionnaire.ajoutertache(
"Réunion d'équipe",
"Présenter le nouveau projet",
Priorite.URGENTE,
datetime.now() + timedelta(days=1),
["travail", "reunion"]
)
print(f"Tâche ajoutée: {t2}")
t3 = gestionnaire.ajoutertache(
"Faire les courses",
"",
Priorite.BASSE,
datetime.now() + timedelta(days=2),
["personnel"]
)
print(f"Tâche ajoutée: {t3}")
# Liste toutes les tâches
print("n=== Toutes les tâches ===n")
for tache in gestionnaire.listertaches():
print(tache)
# Modification de statut
print("n=== Modification de statuts ===n")
gestionnaire.modifierstatut(t2.id, StatutTache.ENCOURS)
print(f"Tâche {t2.id} passée en cours")
gestionnaire.modifierstatut(t3.id, StatutTache.TERMINEE)
print(f"Tâche {t3.id} terminée")
# Filtrage
print("n=== Tâches à faire ===n")
for tache in gestionnaire.listertaches(statut=StatutTache.AFAIRE):
print(tache)
print("n=== Tâches urgentes (3 jours) ===n")
for tache in gestionnaire.tachesurgentes(3):
print(tache)
# Statistiques
print("n=== Statistiques ===n")
stats = gestionnaire.statistiques()
print(f"Total: {stats['total']}")
print(f"Par statut: {stats['parstatut']}")
print(f"Par priorité: {stats['parpriorite']}")
print(f"En retard: {stats['enretard']}")
print(f"Urgentes: {stats['urgentes']}")
# Sauvegarde
print("n=== Sauvegarde ===n")
gestionnaire.sauvegarder("/tmp/taches.json")
print("Tâches sauvegardées dans /tmp/taches.json")
if name == "main":
demogestionnaire()
"""
testtaskmanager.py - Tests unitaires pour le gestionnaire de tâches
"""
import pytest
from datetime import datetime, timedelta
from taskmanager import (
Tache,
GestionnaireTaches,
Priorite,
StatutTache
)
class TestTache:
"""Tests pour la classe Tache."""
def testcreationtache(self):
"""Test de création d'une tâche."""
tache = Tache(
"Test",
"Description test",
Priorite.HAUTE
)
assert tache.titre == "Test"
assert tache.description == "Description test"
assert tache.priorite == Priorite.HAUTE
assert tache.statut == StatutTache.AFAIRE
def testtacheenretard(self):
"""Test de détection de retard."""
# Tâche avec échéance passée
tacheretard = Tache(
"Retard",
echeance=datetime.now() - timedelta(days=1)
)
assert tacheretard.estenretard() is True
# Tâche avec échéance future
tachefuture = Tache(
"Future",
echeance=datetime.now() + timedelta(days=1)
)
assert tachefuture.estenretard() is False
# Tâche sans échéance
tachesans = Tache("Sans échéance")
assert tachesans.estenretard() is False
def testjoursrestants(self):
"""Test du calcul des jours restants."""
tache = Tache(
"Test",
echeance=datetime.now() + timedelta(days=5)
)
assert tache.joursrestants() == 5
tachesans = Tache("Sans échéance")
assert tachesans.joursrestants() is None
class TestGestionnaireTaches:
"""Tests pour le gestionnaire de tâches."""
@pytest.fixture
def gestionnaire(self):
"""Fixture pour créer un gestionnaire vide."""
return GestionnaireTaches()
@pytest.fixture
def gestionnaireavectaches(self, gestionnaire):
"""Fixture pour créer un gestionnaire avec des tâches."""
gestionnaire.ajoutertache("Tâche 1", priorite=Priorite.HAUTE)
gestionnaire.ajoutertache("Tâche 2", priorite=Priorite.BASSE)
gestionnaire.ajoutertache("Tâche 3", priorite=Priorite.MOYENNE)
return gestionnaire
def testajoutertache(self, gestionnaire):
"""Test d'ajout de tâche."""
tache = gestionnaire.ajoutertache(
"Nouvelle tâche",
"Description",
Priorite.HAUTE
)
assert tache.id == 1
assert tache.titre == "Nouvelle tâche"
assert len(gestionnaire.taches) == 1
def testobtenirtache(self, gestionnaireavectaches):
"""Test de récupération de tâche."""
tache = gestionnaireavectaches.obtenirtache(1)
assert tache is not None
assert tache.titre == "Tâche 1"
tacheinexistante = gestionnaireavectaches.obtenirtache(999)
assert tacheinexistante is None
def testsupprimertache(self, gestionnaireavectaches):
"""Test de suppression de tâche."""
assert gestionnaireavectaches.supprimertache(1) is True
assert len(gestionnaireavectaches.taches) == 2
assert gestionnaireavectaches.supprimertache(999) is False
def testmodifierstatut(self, gestionnaireavectaches):
"""Test de modification de statut."""
assert gestionnaireavectaches.modifierstatut(1, StatutTache.TERMINEE)
tache = gestionnaireavectaches.obtenirtache(1)
assert tache.statut == StatutTache.TERMINEE
def testlistertachesfiltres(self, gestionnaireavectaches):
"""Test de filtrage des tâches."""
# Modifier quelques statuts
gestionnaireavectaches.modifierstatut(1, StatutTache.TERMINEE)
# Filtrer par statut
afaire = gestionnaireavectaches.listertaches(
statut=StatutTache.AFAIRE
)
assert len(afaire) == 2
# Filtrer par priorité
hautes = gestionnaireavectaches.listertaches(
priorite=Priorite.HAUTE
)
assert len(hautes) == 1
def testtachesurgentes(self, gestionnaire):
"""Test des tâches urgentes."""
# Tâche urgente (2 jours)
gestionnaire.ajoutertache(
"Urgente",
echeance=datetime.now() + timedelta(days=2)
)
# Tâche non urgente (5 jours)
gestionnaire.ajoutertache(
"Non urgente",
echeance=datetime.now() + timedelta(days=5)
)
urgentes = gestionnaire.tachesurgentes(3)
assert len(urgentes) == 1
def teststatistiques(self, gestionnaireavectaches):
"""Test des statistiques."""
gestionnaireavectaches.modifierstatut(1, StatutTache.TERMINEE)
stats = gestionnaireavectaches.statistiques()
assert stats["total"] == 3
assert stats["parstatut"]["terminée"] == 1
assert stats["parstatut"]["à faire"] == 2
if name == "main":
pytest.main([file, "-v"])
# pytest.ini - Configuration pytest
[pytest]
testpaths = tests
pythonfiles = test.py
pythonclasses = Test
pythonfunctions = test
addopts =
-v
--strict-markers
--cov=.
--cov-report=html
--cov-report=term-missing
# pyproject.toml - Configuration des outils de qualité de code
[tool.black]
line-length = 88
target-version = ['py310']
include = '.pyi?$'
extend-exclude = '''
/(
# directories
.eggs
| .git
| .hg
| .mypycache
| .tox
| .venv
| build
| dist
)/
'''
[tool.mypy]
pythonversion = "3.10"
warnreturnany = true
warnunusedconfigs = true
disallowuntypeddefs = true
disallowincompletedefs = true
checkuntypeddefs = true
noimplicitoptional = true
warnredundantcasts = true
warnunusedignores = true
warnnoreturn = true
strictequality = true
[tool.pytest.inioptions]
minversion = "7.0"
addopts = "-ra -q --strict-markers"
testpaths = ["tests"]
"""
bestpractices.py - Démonstration des bonnes pratiques Python
"""
# PEP 8: Style de code
# - 4 espaces pour l'indentation (pas de tabulations)
# - Lignes max 79 caractères (88 avec Black)
# - 2 lignes blanches entre fonctions de niveau module
# - 1 ligne blanche entre méthodes de classe
# PEP 257: Docstrings
# - Toujours utiliser des docstrings pour modules, classes, fonctions
# - Format: triple guillemets """
# - Première ligne: résumé court
# - Lignes suivantes: détails
from typing import List, Optional, Protocol
from dataclasses import dataclass
from enum import Enum
# PEP 484: Type Hints
# - Toujours typer les paramètres et retours de fonction
# - Utiliser Optional[T] pour les valeurs pouvant être None
# - Utiliser Union[T1, T2] pour plusieurs types possibles
@dataclass
class Config:
"""
Configuration de l'application.
Utilise @dataclass pour éviter le code boilerplate.
"""
nom: str
version: str
debug: bool = False
maxconnexions: int = 100
class Loggable(Protocol):
"""
Protocol pour les objets loggables (PEP 544).
Les protocols définissent des interfaces sans héritage.
"""
def log(self, message: str) -> None:
"""Log un message."""
...
# Bonnes pratiques de nommage
# - snakecase pour fonctions et variables
# - PascalCase pour classes
# - MAJUSCULES pour constantes
# - prefixe pour méthodes privées
CONSTANTEGLOBALE: int = 42
def fonctionpublique(parametre: str) -> str:
"""Fonction accessible publiquement."""
return fonctionprivee(parametre)
def fonctionprivee(parametre: str) -> str:
"""Fonction d'usage interne (convention)."""
return parametre.upper()
# Context managers (with statement)
class GestionnaireRessource:
"""
Gestionnaire de ressource utilisant le protocole context manager.
Assure le nettoyage même en cas d'erreur.
"""
def init(self, nom: str) -> None:
"""Initialise le gestionnaire."""
self.nom = nom
self.ressource: Optional[str] = None
def enter(self) -> "GestionnaireRessource":
"""Appelé au début du bloc with."""
print(f"Ouverture de {self.nom}")
self.ressource = f"Ressource {self.nom}"
return self
def exit(self, exctype, excval, exctb) -> None:
"""Appelé à la fin du bloc with."""
print(f"Fermeture de {self.nom}")
self.ressource = None
# EAFP vs LBYL
# Python préfère EAFP (Easier to Ask for Forgiveness than Permission)
# plutôt que LBYL (Look Before You Leap)
def eafpexemple(dictionnaire: dict, cle: str) -> Optional[str]:
"""Exemple EAFP (préféré en Python)."""
try:
return dictionnaire[cle]
except KeyError:
return None
def lbylexemple(dictionnaire: dict, cle: str) -> Optional[str]:
"""Exemple LBYL (moins pythonique)."""
if cle in dictionnaire:
return dictionnaire[cle]
return None
# List comprehensions vs boucles for
# Préférer les comprehensions quand c'est lisible
def filtrernombrespythonique(nombres: List[int]) -> List[int]:
"""Version pythonique avec comprehension."""
return [n for n in nombres if n > 0 and n % 2 == 0]
def filtrernombresverbeux(nombres: List[int]) -> List[int]:
"""Version verbeuse (éviter si possible)."""
resultat = []
for n in nombres:
if n > 0:
if n % 2 == 0:
resultat.append(n)
return resultat
if name == "main":
# Test dataclass
config = Config("MonApp", "1.0.0", debug=True)
print(f"Config: {config}")
# Test context manager
with GestionnaireRessource("test.txt") as gestion:
print(f"Utilisation de: {gestion.ressource}")
# Comparaison EAFP vs LBYL
dico = {"cle1": "valeur1"}
print(eafpexemple(dico, "cle1"))
print(eafpexemple(dico, "cleinexistante"))
# Comprehensions
nombres = [-2, -1, 0, 1, 2, 3, 4, 5, 6]
print(filtrernombrespythonique(nombres))
Vous avez maintenant une base solide en Python avec :
Continuez à pratiquer avec des projets réels et n’hésitez pas à explorer les bibliothèques de l’écosystème Python.
Cet article est vivant — corrections, contre-arguments et retours de production sont les bienvenus. Trois canaux, choisissez celui qui vous convient.