Apprendre Python : Modules

Apprenez à structurer vos projets Python en utilisant des modules et packages pour organiser votre code de manière efficace.

Icône de calendrier
Intermédiaire
4 chapitres

Qu’est-ce qu’un module ?

En Python, les modules permettent de diviser un programme en parties plus petites et indépendantes qui peuvent être réutilisées dans d’autres parties du programme.

Un module est représenté par un fichier .py qui peut contenir des variables, des fonctions, etc., qui peuvent être importées dans d’autres modules.

De ce fait, chaque module a son propre contexte. Il ne peut donc pas interférer avec d’autres modules ni polluer la portée globale.

Cela permet de structurer et d’organiser le code de manière plus claire et de prévenir les conflits de noms entre les différentes parties du programme.

3 grands types de modules

Dans un projet Python, on distingue trois grands types de modules : « Core Modules », « Local Modules » et « Third-Party Modules ».

1. Core Modules

Les Core Modules sont les modules fournis avec Python par défaut. Ils font partie intégrante de la bibliothèque standard de Python.

Bien que ces modules fassent partie intégrante du langage, ils doivent être importés avant de pouvoir être utilisés dans le code.

Le tableau suivant répertorie certains des Core Modules principaux de Python.

ModuleDescription
osFournit des fonctions pour interagir avec le système d’exploitation, comme la gestion des fichiers et des processus.
sysFournit des fonctions et des variables liées à l’environnement d’exécution Python, comme la gestion des arguments de ligne de commande et l’interaction avec l’interpréteur Python.
mathOffre des fonctions mathématiques standard, comme les fonctions trigonométriques et logarithmiques.
datetimeGère les dates et les heures, permet de manipuler et formater les objets de date/heure.
jsonPermet de lire, écrire et manipuler des données au format JSON (JavaScript Object Notation).
reFournit des fonctions pour la manipulation d’expressions régulières, utile pour le traitement de texte et la recherche de motifs.

2. Local Modules

Les modules locaux sont des modules créés localement (par vous) dans votre application Python.

Ces modules incluent différentes fonctionnalités de votre application dans des fichiers et dossiers distincts.

3. Third-Party Modules

Vous pouvez également décider de packager et de distribuer vos modules locaux, afin que la communauté Python puisse les utiliser : ils deviendront alors des third-party modules (modules tiers).

Nous détaillerons comment installer des modules tiers via le gestionnaire de paquets pip au chapitre suivant.

Création d’un module

Créer un fichier 📄 module1.py et placer le code suivant à l’intérieur :

module1.py
copié !
def count_vowels(text):
	vowels = "aeiouAEIOU"
	count = 0
	for char in text:
		if char in vowels:
			count += 1
	return count

Cette fonction retourne le nombre de voyelles contenues dans une chaîne de caractères.

Importation et utilisation d’un module

Pour importer un module Python, on procède en utilisant le mot-clé import, suivi d’un nom de variable, vouée à recevoir le contenu du module.

copié !
import module1

Import complet

L’import complet d’un module consiste par définition à importer l’ensemble de son contenu.

copié !
import module1

print(module1.count_vowels("laConsole"))

La variable module1 contient l’ensemble des éléments du module (variables, fonctions, etc.). Pour accéder à un élément, on doit donc utiliser la notation <nom_paquet>.<nom_element>.

Import partiel

L’import partiel d’un module consiste à importer uniquement les éléments qui nous intéressent. Cela est rendu possible via l’usage du mot-clé from.

module1.py
copié !
def count_vowels(text):
	vowels = "aeiouAEIOU"
	count = 0
	for char in text:
		if char in vowels:
			count += 1
	return count

def count_consonants(text):
	consonants = "bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ"
	count = 0
	for char in text:
		if char in consonants:
			count += 1
	return count
copié !
from module1 import count_vowels

print(count_vowels("laConsole")) # ✅ Affiche 4
print(count_consonants("laConsole")) # ❌ Erreur => 'count_consonants' is not defined

L’élément importé étant individuellement spécifié, il n’est plus nécessaire de préfixer son usage du nom du module (ici module1).

Pour importer plusieurs éléments, il suffit de les séparer par des virgules :

copié !
from module1 import count_vowels, count_consonants

print(count_vowels("laConsole")) # ✅ Affiche 4
print(count_consonants("laConsole")) # ✅ Affiche 5

Du module au package

Un package est simplement un dossier contenant un ensemble de modules organisés dans des sous-dossiers et éventuellement des fichiers supplémentaires, le tout étant destiné à être utilisé comme un ensemble cohérent.

Les packages permettent de structurer le code de manière plus organisée, surtout lorsqu’un projet devient complexe.

Créer un package

Créer un package est une tâche relativement simple :

  1. Créer un dossier pour le package : Le nom du dossier sera le nom du package.
  2. Ajouter un fichier 📄 __init__.py : Ce fichier est requis pour que Python reconnaisse le dossier comme un package. Il peut rester vide ou contenir du code d’initialisation, des importations d’autres modules du package, ou des éléments de configuration (variables et fonctions partagées).
  3. Ajouter des modules au package : Créez des fichiers portant l’extension .py dans le dossier du package pour ajouter des modules. Ces modules contiendront les fonctions, variables, etc., à inclure dans le package.

Voici un exemple de structure de package nommé math_utils, dédié à la mise à disposition de fonctions mathématiques sur-mesure :

📂 math_utils/
├── 📄 __init__.py
├── 📄 format.py
└── 📄 geometry.py

📄 __init__.py reste vide :

__init__.py

📄 format.py contient une fonction percentage() retournant le pourcentage associé à une fraction part/total :

format.py
copié !
def percentage(part, total):
	return f"{part / total * 100}%"

📄 geometry.py contient :

  • Une fonction area_circle() calculant l’aire d’un cercle.
  • Une fonction area_square() calculant l’aire d’un carré.
geometry.py
copié !
import math

def area_circle(radius):
	return math.pi * radius * radius

def area_square(side):
	return side * side

Importer un package

La méthode d’import de package varie selon le contenu du fichier d’initialisation 📄 __init__.py.

Imports à la carte

Si 📄 __init__.py est vide, il n’ajoute pas de fonctionnalités ou d’initialisation supplémentaires au package mais permet uniquement à Python de reconnaître le répertoire contenant ce fichier comme un package.

Ce code provoquera alors une erreur :

copié !
import math_utils

# ❌ Erreur => module 'math_utils' has no attribute 'geometry'
print(math_utils.geometry.area_circle(2))

Il faudra alors importer explicitement avec import les modules souhaités :

copié !
import math_utils.geometry

print(math_utils.geometry.area_circle(2)) # ✅ OK

# ❌ Erreur => module 'math_utils' has no attribute 'format'
print(math_utils.format.percentage(2, 10))

L’usage du mot-clé from propose la syntaxe alternative suivante :

copié !
from math_utils import geometry

print(geometry.area_circle(2)) 

L’utilisation de from et import combinés permet d’exploiter le module geometry sans avoir besoin de faire référence au package parent math_utils à chaque appel de fonction. Cela simplifie l’accès aux fonctions du module tout en maintenant une bonne lisibilité du code.

Pré-imports

Il est également possible d’importer les modules relatifs à un package au niveau du fichier d’initialisation 📄 __init__.py.

__init__.py
copié !
from math_utils.geometry import area_circle, area_square
from math_utils.format import percentage
Notation alternative (import relatif)

math_utils.geometry constitue un import absolu car on précise directement le chemin complet du module à importer, en commençant par la racine du package (math_utils).

Il est également possible d’écrire .geometry, constituant un import relatif, où le point . représente le package courant.

__init__.py
copié !
from .geometry import area_circle, area_square
from .format import percentage

Cela aura pour effet de rendre accessible depuis le paquet, l’ensemble des modules importés dans le 📄 __init__.py, facilitant son usage :

copié !
import math_utils

print(math_utils.geometry.area_circle(2)) # ✅ OK

Il est même possible d’omettre le nom du module lors de l’appel d’une de ses composantes.

copié !
import math_utils

print(math_utils.area_circle(2)) # ✅ OK
Charger tous les modules

Le caractère * permet de charger l’ensemble des éléments d’un module ou modules d’un package.

copié !
# Importer l'ensemble des modules d'un paquet
from math_utils import *
copié !
# Importer l'ensemble des éléments d'un module
from .geometry import *

S’il me paraît important de le préciser dans l’éventualité où vous rencontrez cette syntaxe un jour, cette approche reste à proscrire autant que possible dans un souci de clarté et de performance tout en limitant les conflits de noms.

Imports à la carte VS pré-imports

Voici un tableau résumant les avantages et inconvénients de l’importation, ou pas, de modules via le fichier 📄 __init__.py :

ImportDescriptionAvantagesInconvénients
Import à la carteLe fichier 📄 __init__.py est présent mais ne contient aucun code d’importation.- Le package est simple et léger. - Pas de risque de conflits de noms. - Moins de maintenance nécessaire pour le fichier d’initialisation.- Nécessite des importations explicites pour chaque module lorsque utilisé ailleurs. - Peut compliquer l’accès aux modules du package.
Pré-importLe fichier 📄 __init__.py importe certains modules pour les exposer directement.- Simplifie l’accès aux modules et fonctions du package. - Offre une interface plus propre et plus pratique. - Facilite la réorganisation des modules.- Peut exposer plus de fonctions que nécessaire, entraînant des conflits de noms potentiels. - Augmente le risque d’importer des éléments non utilisés, ce qui peut alourdir le package.

Alias

Lorsque vous importez un package ou un module, vous avez la possibilité de lui donner un alias. Cela aura pour effet de le renommer.

Alias de package
copié !
import math_utils as mu

print(mu.geometry.area_circle(2))
Alias de module
copié !
from math_utils import geometry as geo

print(geo.area_circle(2))