Introduction aux modules
Un module Python est un fichier contenant du code réutilisable, portant l'extension .py. Il encapsule des variables, des fonctions et des classes que d'autres scripts peuvent importer pour étendre leurs fonctionnalités. L'utilisation de modules permet de structurer logiquement un projet, améliorant ainsi sa lisibilité et sa maintenabilité.
# Fichier : salutations.py
def dire_bonjour(prenom):
return f"Bonjour, {prenom} !"
# Fichier : programme_principal.py
import salutations
message = salutations.dire_bonjour("Marie")
print(message) # Affiche : Bonjour, Marie !
Origine des modules
Les modules Python se divisent en trois catégories selon leur provenance :
- Bibliothèque standard : Python embarque nativement de nombreux modules comme
math,datetime,osoujson, accessibles sans installation préalable.
import math
racine = math.isqrt(49) # Racine carrée entière
print(racine) # Affiche : 7
- Modules tiers : Développés par la communauté, ils s'installent via
pipet offrent des fonctionnalités spécialisées (traitement de données, requêtes HTTP, etc.).
# Installation depuis le terminal
# pip install httpx
import httpx
reponse = httpx.get("https://httpbin.org/get")
print(reponse.status_code) # Affiche : 200
- Modules locaux : Vos propres fichiers Python, créés selon les besoins spécifiques de votre projet.
# Fichier : calculs.py
def additionner(a, b):
return a + b
# Fichier : programme_principal.py
from calculs import additionner
print(additionner(10, 20)) # Affiche : 30
Mécanismes d'importation
L'instruction import offre plusieurs syntaxes pour charger des modules. Considérons un module exemple :
# Fichier : utils.py
CONSTANTE = "valeur_initiale"
def afficher_constante():
print(CONSTANTE)
def modifier_constante():
global CONSTANTE
CONSTANTE = "nouvelle_valeur"
print(CONSTANTE)
class Gestionnaire:
def executer(self):
print("Méthode executer() de la classe Gestionnaire")
Importation complète d'un module
import utils
# Accès via le préfixe du module
v = utils.CONSTANTE
print(v) # valeur_initiale
utils.afficher_constante()
utils.modifier_constante() # nouvelle_valeur
gest = utils.Gestionnaire()
gest.executer() # Méthode executer() de la classe Gestionnaire
Lors de l'importation, Python crée un espace de noms dédié au module. Les noms définis dans le module sont isolés de ceux du script appelant, évitant ainsi les conflits. Le code du module n'est exécuté qu'une seule fois, même si le module est importé plusieurs fois.
Utilisation d'alias
Pour les modules au nom long, un alias simplifie les références :
import utils as u
u.afficher_constante() # Accès via l'alias
Importation sélective
from utils import CONSTANTE, afficher_constante
print(CONSTANTE)
afficher_constante()
Avec cette syntaxe, les noms importés sont directement accessibles sans préfixe. Attention : si le script appelant définit une variable du même nom, elle écrasera celle importée.
Importation globale avec l'astérisque
from utils import *
Cette approche importe tous les noms publics du module. Elle est déconseillée car elle peut provoquer des collisions de noms difficiles à déboguer.
Contrôle avec __all__
Le créateur d'un module peut définir __all__ pour restreindre ce que * importe :
# Fichier : utils.py
__all__ = ['CONSTANTE', 'afficher_constante']
CONSTANTE = 42
def afficher_constante():
print(CONSTANTE)
def modifier_constante():
global CONSTANTE
CONSTANTE = 0
class Gestionnaire:
pass
# Seuls CONSTANTE et afficher_constante seront importés
from utils import *
afficher_constante() # Fonctionne
# modifier_constante() # Erreur : non défini
# Gestionnaire() # Erreur : non défini
Distinguer script principal et module importé
La variable __name__ permet d'identifier le contexte d'exécution :
# Dans utils.py
print(__name__) # "utils" si importé, "__main__" si exécuté directement
if __name__ == '__main__':
# Code exécuté uniquement quand le fichier est lancé directement
afficher_constante()
Imports circulaires
Un import circulaire survient quand deux modules se référencent mutuellement, créant une dépendance impossible à résoudre. Cette situation indique généralement un problème de conception.
# Fichier : module_a.py
from module_b import fonction_b
def fonction_a():
fonction_b()
# Fichier : module_b.py
from module_a import fonction_a
def fonction_b():
fonction_a()
# ImportError: cannot import name 'fonction_a' from partially initialized module 'module_a'
Plusieurs stratégies permettent de résoudre ce problème :
- Réorganisation du code : Extraire les éléments partagés dans un troisième module commun.
- Import différé : Placer l'import à l'intérieur d'une fonction plutôt qu'au niveau du module.
# module_a.py
def fonction_a():
from module_b import fonction_b
fonction_b()
- Import dynamique avec importlib :
from importlib import import_module
def fonction_a():
mod = import_module('module_b')
mod.fonction_b()
- Déplacement des imports en fin de fichier : Le cache des modules de Python permet parfois de contourner le problème.
Recherche et résolution des chemins
Lorsqu'un module est importé, Python le cherche dans cet ordre :
- Le cache des modules déjà chargés en mémoire
- Les modules intégrés à l'interpréteur
- Les répertoires listés dans
sys.path
import sys
for chemin in sys.path:
print(chemin)
La liste sys.path inclut le répertoire du script courant, les chemins de la bibliothèque standard et ceux des packages tiers. Vous pouvez y ajouter des répertoires personnalisés :
import sys
sys.path.append('/chemin/vers/mes/modules')
import mon_module_personnalise
Un module déjà chargé en mémoire reste disponible même si le fichier source est supprimé, car Python conserve une référence en cache.
Chemins absolus et relatifs
Chemin relatif
.désigne le répertoire actuel..désigne le répertoire parent
# Exemples
"./donnees/fichier.txt" # Sous-dossier du répertoire courant
"../config/parametres.json" # Dossier parent puis sous-dossier
Chemin absolu
# Linux/macOS
chemin_absolu = "/home/utilisateur/projets/data/fichier.csv"
# Windows
chemin_absolu = "C:\\Users\\Marie\\Documents\\fichier.csv"
Imports absolus vs relatifs dans les packages
L'import absolu utilise le chemin complet depuis la racine du projet. L'import relatif utilise la notation . pour naviguer entre modules d'un même package :
# Import absolu
from mon_package.sous_module import ma_fonction
# Import relatif (uniquement dans un package)
from . import sous_module # Module voisin
from .. import module_parent # Module du package parent
Un fichier contenant des imports relatifs ne peut pas être exécuté comme script principal ; il doit obligatoirement être importé.
Packages Python
Concept et structure
Un package est un répertoire contenant des modules et un fichier __init__.py, qui signale à Python que le dossier doit être traité comme une unité importable. Les packages permettent de hiérarchiser le code en espaces de noms imbriqués.
mon_application/
├── __init__.py
├── base_donnees.py
├── authentification.py
└── api/
├── __init__.py
├── routes.py
└── validateurs.py
Utilisation des packages
Importer un package revient à exécuter son fichier __init__.py. Les noms définis dans ce fichier deviennent accessibles via le nom du package :
# Fichier : mon_application/__init__.py
from .base_donnees import connexion
from .authentification import verifier_token
# Fichier : programme_principal.py
import mon_application
mon_application.verifier_token("abc123")
L'importation d'un package déclenche la création d'un espace de noms dédié, dans lequel les noms du fichier __init__.py sont enregistrés. Le package peut ensuite être utilisé comme tout autre module.