Fonctions avancées, portée des variables et environnement de développement
Objectifs
À la fin de cette séance, vous serez capables de :
- Comprendre la portée des variables (scope) et éviter les bugs classiques associés
- Expliquer le principe de la récursivité (niveau lecture, pas d’écriture attendue à ce stade)
- Configurer un environnement virtuel isolé avec
venv - Remplacer des
print()de débogage par un système de journalisation aveclogging
1. Portée des variables (scope)
La portée d’une variable décrit la zone du programme où elle est accessible.
Variable locale
def calculer():
resultat = 10 * 2
return resultat
print(calculer())
resultat existe uniquement dans la fonction.
Variable globale
seuil = 50
def depasse(valeur):
return valeur > seuil
print(depasse(60))
Les variables globales peuvent être utilisées, mais elles rendent le code moins explicite et plus fragile.
Bonnes pratiques
Il vaut mieux passer les informations nécessaires en arguments plutôt que de dépendre de variables externes.
def depasse(valeur, seuil):
return valeur > seuil
print(depasse(60, 50))
Cas classique de bug
total_mesures = 0
def ajouter_mesure(valeur):
total_mesures = total_mesures + valeur
Ce code provoque une erreur car Python considère total_mesures comme une variable locale à la fonction.
La bonne solution consiste à renvoyer une valeur et à la réaffecter à l’extérieur :
def ajouter_mesure(total, valeur):
return total + valeur
total_mesures = 0
total_mesures = ajouter_mesure(total_mesures, 10)
2. Récursivité — aperçu
Niveau attendu à ce stade : comprendre et expliquer un code récursif simple, pas nécessairement en écrire un en autonomie.
La récursivité consiste à faire appel à la fonction elle-même. Cela permet de résoudre certains problèmes en les décomposant en sous-problèmes plus petits.
Pour qu’une fonction récursive fonctionne correctement, il faut :
- un cas de base qui arrête la récursion
- un appel récursif avec un problème plus petit
def compte_a_rebours(n):
if n == 0:
print("C'est parti !")
else:
print(n)
compte_a_rebours(n - 1)
compte_a_rebours(3)
3. Environnements virtuels avec venv
Pourquoi un environnement dédié ?
Quand on travaille sur plusieurs projets Python, chacun peut avoir besoin de versions différentes des mêmes librairies. Installer toutes les librairies directement sur la machine (globalement) finit tôt ou tard par créer des conflits.
Un environnement virtuel résout ce problème : chaque projet a son propre dossier de dépendances, isolé des autres projets et du système.
Créer et activer un environnement
python -m venv env
Sous Windows :
env\Scripts\activate
Sous macOS / Linux :
source env/bin/activate
Une fois activé, l’invite de commande affiche généralement le nom de l’environnement entre parenthèses, par exemple (env).
Installer des paquets
pip install requests
Le paquet est installé uniquement dans env/, pas globalement sur la machine.
Dépendances du projet
pip freeze > requirements.txt
pip install -r requirements.txt
Désactiver l’environnement
deactivate
Bonnes pratiques
- Un environnement virtuel par projet.
- Ne jamais versionner (
git) le dossierenv/— seulementrequirements.txt. - Toujours activer l’environnement avant d’installer des paquets.
4. Journalisation avec logging
Pourquoi pas juste des print() ?
Les print() sont pratiques pour un débogage ponctuel, mais ont des limites : pas de niveaux de gravité, pas de formatage automatique de la date/heure, et il faut les supprimer un par un avant de livrer le code.
Le module logging répond à ces limites.
Niveaux de gravité
DEBUG: informations détaillées pour le débogageINFO: confirmation que tout fonctionneWARNING: quelque chose d’inattenduERROR: erreur sérieuseCRITICAL: erreur très grave
Exemple
import logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='app.log'
)
logger = logging.getLogger(__name__)
logger.debug("Message de debug")
logger.info("Opération réussie")
logger.warning("Attention !")
logger.error("Erreur détectée")
Exemple avec gestion d’erreur
def traiter_donnees(donnees):
logger.debug(f"Début du traitement avec {len(donnees)} éléments")
try:
resultat = sum(donnees) / len(donnees)
logger.info(f"Traitement réussi. Moyenne : {resultat}")
return resultat
except ZeroDivisionError:
logger.error("Données vides, impossible de calculer")
return None
5. Pour aller plus loin (optionnel)
Cette partie n’est pas couverte en classe — à explorer en autonomie si vous finissez les exercices en avance, ou après la séance.
Poetry
Poetry combine gestion des dépendances, environnement virtuel et fichier de verrouillage
(poetry.lock) pour garantir que chacun installe exactement les mêmes versions.
Documentation : python-poetry.org
pre-commit
pre-commit exécute des vérifications automatiques (formatage, style, erreurs de
syntaxe) avant chaque git commit, et bloque le commit si un problème est détecté.
pip install pre-commit
pre-commit install
Intégration continue (CI)
La CI teste automatiquement votre code à chaque push, avant toute fusion. Des
outils comme GitHub Actions exécutent les mêmes vérifications que pre-commit, mais
sur un serveur, pour tout le monde.
TD
Le TD de la séance est disponible ici.