Les modules : pourquoi découper en fichiers, import, from et as
- Expliquer ce qu’est un module et pourquoi on découpe un projet en plusieurs fichiers
- Créer son propre module et en importer des fonctions et des constantes
- Distinguer
import x,from x import yetimport x as z, et savoir quand utiliser chacun - Comprendre que le code au niveau supérieur d’un module s’exécute au moment de l’import
Qu’allons-nous apprendre ?
- Ce qu’est un module.
- Pourquoi on découpe un projet en plusieurs fichiers.
- Trois formes d’import :
import,from ... import,as. - Ce qui se passe exactement quand Python importe un fichier.
Pourquoi en avons-nous besoin ?
Nous avons déjà utilisé des modules : import os, import logging, import socket. Aujourd’hui, nous allons écrire les nôtres.
Un vrai projet fait des centaines et des milliers de lignes. Un seul fichier énorme est :
- Difficile à parcourir — où est la fonction qui envoie un e-mail ? Ligne 1 840 ?
- Difficile pour le travail en équipe — trois développeurs modifient le même fichier et se gênent (dans Git, dans le thème Git).
- Difficile à réutiliser — une bonne fonction de validation est « coincée » dans le fichier d’un jeu.
Découper en fichiers, c’est la séparation des responsabilités au niveau du projet. Dans les thèmes backend et bases de données, le serveur sera découpé exactement ainsi : main.py (les routes), db.py (l’accès à la base de données), models.py.
Explication à partir des bases
Qu’est-ce qu’un module ?
Module = n’importe quel fichier .py. C’est tout. Chaque fichier Python que vous avez écrit est déjà un module, et vous pouvez en importer des fonctions, des constantes et des variables dans un autre fichier.
- Le nom du module = le nom du fichier sans
.py. Le fichierprices.py→ le moduleprices. - C’est pourquoi les noms de fichiers doivent être des noms Python valides :
snake_case, sans tirets (my-utils.pyne s’importera pas !), et sans commencer par un chiffre.
Un seul fichier énorme = un seul tiroir avec tous les ustensiles : couteaux, cuillères, pinces, ciseaux. Pour trouver quelque chose, on fouille dans tout. Les modules = des tiroirs séparés avec des étiquettes : « couverts », « ustensiles de pâtisserie », « épices ». import = « apporte-moi quelque chose de ce tiroir ».
Trois formes d’import
| Forme | Comment on l’utilise ensuite | Quand |
|---|---|---|
import prices | prices.add_vat(100) | Par défaut. On sait clairement d’où vient chaque nom. |
from prices import add_vat | add_vat(100) | Quand on utilise souvent quelques noms précis. |
import prices as p / from prices import add_vat as vat | p.add_vat(100) / vat(100) | Un nom long ou une collision de noms. Conventions connues : import numpy as np. |
Que se passe-t-il au moment de l’import ?
- Python cherche le fichier : d’abord dans le dossier du script que vous avez lancé, puis dans les bibliothèques de Python.
- Python exécute tout le fichier de haut en bas, une fois : il crée les fonctions et les constantes, et exécute tout code situé au niveau supérieur.
- Le résultat est conservé. Un autre
importdu même module ne l’exécute pas à nouveau.
Le point 2 est très important, et nous verrons ses conséquences dans l’erreur courante.
__pycache__
Après le premier import, un dossier nommé __pycache__ avec des fichiers .pyc apparaîtra. C’est le bytecode (souvenez-vous du thème sur l’ordinateur et le système d’exploitation !) des modules, enregistré pour que l’import suivant soit plus rapide. N’y touchez pas, et dans les projets Git, ignorez-le (nous le verrons dans .gitignore dans le thème Git).
Syntaxe
# file: prices.py (the module)
VAT_RATE = 0.17
def add_vat(amount):
return amount * (1 + VAT_RATE)# file: main.py (uses the module; same folder)
import prices
prices.add_vat(100)
from prices import add_vat, VAT_RATE
add_vat(100)
import prices as p
p.add_vat(100)
from prices import add_vat as with_vat
with_vat(100)L’ordre en usage en haut d’un fichier : d’abord les imports de la bibliothèque standard (os, json), une ligne vide, puis les imports de vos propres modules.
Premier exemple de code
Créez un dossier week-2/shop/, avec deux fichiers dedans.
week-2/shop/prices.py :
VAT_RATE = 0.17
def add_vat(amount):
return round(amount * (1 + VAT_RATE), 2)
def apply_discount(amount, percent):
return round(amount - amount * percent / 100, 2)week-2/shop/main.py :
import prices
base_price = 200
discounted = prices.apply_discount(base_price, 10)
final_price = prices.add_vat(discounted)
print("Base:", base_price)
print("After 10% discount:", discounted)
print("With VAT (" + str(prices.VAT_RATE) + "):", final_price)Exécution (depuis week-2/shop) : python main.py
Sortie :
Base: 200
After 10% discount: 180.0
With VAT (0.17): 210.6Analyse du code ligne par ligne
| Ligne | Explication |
|---|---|
import prices | Python cherche prices.py dans le même dossier que main.py, l’exécute, et crée le nom prices qui représente le module. |
prices.apply_discount(...) | On accède à la fonction par le nom du module, avec un point. Exactement comme os.getcwd(). |
prices.VAT_RATE | Les constantes sont accessibles de la même façon. |
main.py ne définit aucune fonction de prix | Toute la logique des prix est à un seul endroit. Si la TVA change, on ne modifie que prices.py. |
Un autre exemple
Les trois formes d’import, et un second module. Ajoutez week-2/shop/validation.py :
MIN_NAME_LENGTH = 2
def is_valid_price(price):
return isinstance(price, (int, float)) and price >= 0
def is_valid_name(name):
return len(name.strip()) >= MIN_NAME_LENGTHEt mettez à jour main.py :
import prices as p
from validation import is_valid_price, is_valid_name
products = [("Laptop", 3500), ("X", 20), ("Mouse", -5), ("Desk", 900)]
for name, price in products:
if not is_valid_name(name):
print("Skipping invalid name:", repr(name))
elif not is_valid_price(price):
print("Skipping invalid price for", name, ":", price)
else:
print(name, "->", p.add_vat(price), "NIS incl. VAT")Sortie :
Laptop -> 4095.0 NIS incl. VAT
Skipping invalid name: 'X'
Skipping invalid price for Mouse : -5
Desk -> 1053.0 NIS incl. VATAnalyse :
import prices as p— un surnom court. On y accède avecp.add_vat.from validation import is_valid_price, is_valid_name— importe seulement les deux fonctions, et les utilise directement, sans préfixe.repr(name)— affiche la valeur avec des guillemets ('X'), comme%rdans les journaux.- La structure du projet :
prices.py= la logique des prix.validation.py= les règles de validité.main.py= relie et exécute. Chaque fichier a une seule responsabilité.
Erreur courante
Erreur 1 : du code de « test » au niveau supérieur d’un module. Supposons qu’en bas de prices.py vous ayez ajouté un test :
print("Testing prices module:", add_vat(100))Désormais, chaque import prices, depuis n’importe quel fichier, affichera cette ligne, car l’import exécute le fichier. Dans la leçon suivante, nous apprendrons la solution : if __name__ == "__main__":.
Erreur 2 : un nom de fichier qui entre en collision avec un module standard.
Vous avez créé un fichier nommé random.py (ou logging.py, json.py), et dans un autre fichier vous avez écrit import random :
AttributeError: module 'random' has no attribute 'randint'Python trouve votre random.py en premier, dans le même dossier, et l’importe à la place du vrai module. Ne nommez jamais des fichiers avec les noms de modules standards.
Erreur 3 : ModuleNotFoundError.
ModuleNotFoundError: No module named 'prices'Les raisons courantes : le fichier n’est pas dans le même dossier que le script lancé, une faute de frappe dans le nom, ou un nom de fichier avec un tiret (my-prices.py).
Erreur 4 : from module import *. Importe tous les noms du module. On ne sait pas d’où vient chaque nom, et des noms peuvent s’écraser silencieusement. Importez explicitement ce dont vous avez besoin.
Exercices
Les exercices n’utilisent que ce qui a été vu dans cette leçon et les précédentes. Essayez d’abord seul, et n’ouvrez la solution qu’ensuite.
Exercice 1: Le premier module
Créez un fichier prices.py avec la constante VAT_RATE = 0.17 et deux fonctions : add_vat(amount) et apply_discount(amount, percent) (toutes deux arrondissent à deux chiffres). Dans le fichier main.py, importez le module, calculez un prix de 200 après une remise de 10 %, puis ajoutez-lui la TVA.
Résultat attendu (en exécutant main.py) :
After discount: 180.0
With VAT: 210.6Afficher la solution
prices.py
VAT_RATE = 0.17
def add_vat(amount):
return round(amount * (1 + VAT_RATE), 2)
def apply_discount(amount, percent):
return round(amount - amount * percent / 100, 2)main.py
import prices
discounted = prices.apply_discount(200, 10)
final_price = prices.add_vat(discounted)
print("After discount:", discounted)
print("With VAT:", final_price)import prices charge le fichier prices.py qui se trouve dans le même dossier. On accède à son contenu avec un point : prices.add_vat(...).
Exercice 2: Trois formes d’import
Écrivez la même ligne — calculer la TVA sur 100 — dans les trois formes d’import : import, from ... import et import ... as. Quand vaut-il la peine d’utiliser chacune ?
Afficher la solution
import prices
print(prices.add_vat(100))
from prices import add_vat
print(add_vat(100))
import prices as p
print(p.add_vat(100))import prices— on sait toujours clairement d’où vient chaque nom (prices.add_vat). Convient quand on utilise beaucoup de choses du module.from prices import add_vat— on écrit moins. Convient à quelques noms précis que l’on utilise beaucoup.import prices as p— un nom court pour un module au nom long.
Exercice 3: La bibliothèque standard
Sans écrire votre propre module, utilisez les modules intégrés de Python :
math— la racine carrée de 81, etmath.piarrondi à deux chiffres.random— avecrandom.seed(1), tirez un nombre entre 1 et 6.
Résultat attendu :
9.0
3.14
2Afficher la solution
import math
import random
print(math.sqrt(81))
print(round(math.pi, 2))
random.seed(1)
print(random.randint(1, 6))9.0
3.14
2math et random font partie de la bibliothèque standard (Standard Library) — ils sont fournis avec Python, et on les importe exactement comme un module que nous avons écrit nous-mêmes. random.seed(1) fixe le tirage, pour que le résultat soit le même à chaque exécution.
Exercice 4: Du code qui s’exécute à l’import
Dans le fichier helpers.py :
def shout(text):
return text.upper() + "!"
print("helpers loaded")Dans le fichier main.py :
from helpers import shout
print(shout("hi"))- Qu’est-ce qui sera affiché quand vous lancerez
python main.py, et pourquoi ? - Comment empêcher la première ligne ?
Afficher la solution
- Ceci sera affiché :
helpers loaded
HI!import exécute tout le fichier — y compris un print situé au niveau supérieur. Même from helpers import shout exécute tout helpers.py, et pas seulement la fonction.
- Un module destiné à être importé ne doit pas avoir de code au niveau supérieur — seulement des définitions. Supprimez le
print. (Dans la leçon suivante, nous apprendronsif __name__ == "__main__":, qui permet de garder du code de test qui ne s’exécute que lorsque le fichier est lancé directement.)
Exercice 5: Un module de températures
Créez un module temperature.py avec c_to_f(celsius) et avec describe(celsius) qui renvoie "hot" à partir de 30 degrés, "cold" en dessous de 15, et "pleasant" entre les deux. Dans main.py, affichez pour chaque ville la température dans les deux unités et la description.
cities = ["Eilat", "Haifa", "Jerusalem"]
temperatures = [35, 22, 9]Résultat attendu :
Eilat: 35 C / 95.0 F - hot
Haifa: 22 C / 71.6 F - pleasant
Jerusalem: 9 C / 48.2 F - coldAfficher la solution
temperature.py
HOT_FROM = 30
COLD_BELOW = 15
def c_to_f(celsius):
return round(celsius * 9 / 5 + 32, 1)
def describe(celsius):
if celsius >= HOT_FROM:
return "hot"
if celsius < COLD_BELOW:
return "cold"
return "pleasant"main.py
from temperature import c_to_f, describe
cities = ["Eilat", "Haifa", "Jerusalem"]
temperatures = [35, 22, 9]
for i in range(len(cities)):
celsius = temperatures[i]
print(cities[i] + ":", celsius, "C /", c_to_f(celsius), "F -", describe(celsius))La logique des températures est dans le module, et main.py ne fait que l’utiliser et afficher.
Résumé
- Module = un fichier
.py. Le nom du module = le nom du fichier sans.py(snake_case, sans tirets). import m→m.func().from m import func→func().import m as x→x.func().- Python cherche les modules d’abord dans le dossier du script. Ne nommez pas de fichiers
random.py,json.py, etc. - L’import exécute le module une fois. Le code au niveau supérieur s’exécute même lors d’un import.
- Découper en fichiers = la séparation des responsabilités au niveau du projet. Pas d’
import *.