Les modules

Un programme Python est écrit dans un fichier portant l’extension .py. Cependant, pour la plupart des programmes, le recours à un seul fichier n’est pas très pratique car le nombre de lignes de code augmente très rapidement au fur et à mesure de l’ajout de fonctionnalités dans l’application.

Les modules permettent de découper logiquement le code source de l’application à travers plusieurs fichiers. Un module peut même être partagé entre plusieurs applications afin de créer une bibliothèque logiciel réutilisable. Par exemple, Python fournit déjà une bibliothèque standard utilisable par toutes les applications sous la forme de plusieurs modules.

Le module pour prévenir la collision de nom

Un problème récurrent en programmation est celui de la collision de noms. Écrire un programme implique de nommer les différents éléments du programme (variables, paramètres, fonctions, objets…). Si on désire réutiliser des fonctions développées par d’autres personnes, il est possible que l’on soit amené à incorporer des fonctions qui portent le même nom. En Python, si on déclare une fonction avec un nom déjà utilisé pour désigner une autre fonction, cette dernière ne sera plus accessible. On se retrouve confronté à un phénomène de collision des noms.

Pour résoudre cette situation, la solution la plus couramment utilisée consiste à créer des espaces de noms différents pour les deux fonctions. En Python, un module porte un nom qui identifie un espace dans lequel on peut définir des fonctions. Ainsi si les modules module1 et le module2 définissent tous les deux une fonction s’appelant abs(), il sera possible de les distinguer à l’appel en préfixant le nom de la fonction par le nom du module :

module1.abs()
module2.abs()
abs(2)

Dans l’exemple ci-dessus, il est même possible d’appeler la méthode standard abs() qui permet de calculer la valeur absolue d’un nombre.

La bibliothèque standard Python

La bibliothèque standard Python contient déjà beaucoup de modules. La liste ci-dessous énumère les modules les plus utiles à connaître pour débuter en Python :

sys

variables et fonctions pour interagir avec l’interpréteur Python

os

fonctions élémentaires pour interagir avec le système d’exploitation

math

fonctions mathématiques avancées

random

bibliothèque pour la génération de nombres aléatoires

datetime

représentation des dates et du temps

calendar

gestion du calendrier

collections

structures de données supplémentaires pour les séquences et les dictionnaires

Importer un module

Pour accéder à un élément d’un module, il faut d’abord importer le module grâce au mot-clé import. Par exemple, le module sys définit la variable version qui contient la version de l’interpréteur Python. Pour pouvoir y accéder, il faut préalablement importer le module sys :

import sys

print(sys.version)

De même, le module random définit la fonction random() qui retourne un nombre compris entre 0 et 1 :

import random

nombre = random.random()
print(nombre)

L’instruction import peut être placée à n’importe quel endroit dans un programme (y compris dans le corps d’une fonction). Cela permet d’importer le module uniquement lorsqu’il est réellement nécessaire d’accéder à un élément qu’il définit.

Note

Si vous souhaitez importer plusieurs modules, vous pouvez utiliser une seule instruction import en séparant le nom des modules par une virgule :

import os, sys, random

Il est possible de donner un alias au moment de l’import pour référencer le module sous un autre nom.

import random as r

nombre = r.random()
print(nombre)

Importer directement un nom

Parfois, vous ne souhaitez pas importer tout un module et il peut être fastidieux de préfixer systématiquement un nom par le module qui le définit. Il existe la syntaxe from ... import pour résoudre cette situation :

from sys import version

print(version)

Grâce à cette syntaxe, la variable version du module sys est directement accessible par son nom.

Cette syntaxe est également utilisable pour les fonctions :

from math import factorial

f = factorial(5)
print(f)
# Affiche 120

Note

Si vous souhaitez importer plusieurs noms du même module, vous pouvez utiliser une seule instruction from ... import en séparant les noms par une virgule :

from sys import version, platform, exit

Il est possible de donner un alias au moment de l’import pour référencer l’élément importé.

from math import factorial as f
resultat = f(3)
print(resultat)
# Affiche 6

Note

Vous pouvez importer tous les noms définis dans un module en utilisant * :

from math import *

v = factorial(5)
f = floor(10.25)
l = log(2)

print(v, f, l)
# Affiche 120 10 0.6931471805599453

Cette syntaxe n’est pas conseillée car il est difficile de connaître avec certitude la liste des noms importés.

Créer un module à partir d’un fichier

Tout fichier source Python (fichier avec l’extension .py) peut être utilisé comme un module. Il est donc très simple de concevoir une application Python composée de plusieurs fichiers : un fichier sert de fichier principal pour le lancement de l’application tandis que les autres fichiers sont utilisés comme des modules.

Supposons qu’une application python est composée de deux fichiers : app.py qui contient le programme principal et entree_sortie.py qui regroupe les fonctions pour interagir avec l’utilisateur.

Le fichier entree_sortie.py
1
2
3
4
5
6
def demander_nom():
    nom = input("Comment vous appelez-vous ? ")
    return nom

def afficher(*args):
    print(*args)
Le fichier app.py
1
2
3
4
from entree_sortie import demander_nom, afficher

nom = demander_nom()
afficher("Bonjour", nom)

Le nom du module correspond au nom du fichier. Le programme principal dans le fichier app.py peut importer et utiliser les fonctions définies dans le module entree_sortie, c’est-à-dire dans le fichier entree_sortie.py.

Module exécutable

Si un module déclare directement du code en dehors de toute fonction, ce code sera exécuté lors du premier import du module. Si vous le souhaitez, un module peut avoir une comportement différent s’il est exécuté directement par l’interpréteur (comme programme principal) ou s’il est importé depuis un autre fichier. Cela permet de créer des modules exécutables de manière autonome. Pour cela, il suffit de tester la valeur de l’attribut du module appelé __name__. Si cet attribut vaut "__main__" alors cela signifie que le fichier est lancée directement par l’interpréteur. Il n’est pas importé et n’agit donc pas comme un module.

if __name__ == "__main__":
    # Il est possible d'exécuter du code pour un module qui est
    # directement appelé depuis l'interpréteur Python
    pass

Créer un module à partir d’un répertoire

Parfois un module est tellement complexe qu’il peut être organisé dans plusieurs fichiers, chacun agissant comme un sous-module du module principal. Dans ce cas, il faut placer tous les fichiers du module dans un répertoire portant le nom du module. Ce répertoire doit contenir un fichier nommé __init__.py qui représente le point d’entrée du module.

Si nous reprenons notre exemple précédent, nous pouvons créer le module entree_sortie en créant un répertoire du même nom avec, par exemple, les fichiers suivants à l’intérieur :

Le fichier entree_sortie/entree.py
1
2
3
def demander_nom():
    nom = input("Comment vous appelez-vous ? ")
    return nom
Le fichier entree_sortie/sortie.py
1
2
def afficher(*args):
    print(*args)
Le fichier entree_sortie/__init__.py
1
2
from entree_sortie.entree import demander_nom
from entree_sortie.sortie import afficher

Le module entree_sortie est maintenant représenté par une répertoire qui contient plusieurs fichiers. Le fichier __init__.py se limite à importer les fonctions demander_nom() et afficher() dans son propre espace de noms qui est celui du module entree_sortie. Le programme principal n’a pas a être modifié par rapport à l’exemple de la section précédente :

Le fichier app.py
1
2
3
4
from entree_sortie import demander_nom, afficher

nom = demander_nom()
afficher("Bonjour", nom)

Module répertoire exécutable

Si un module est représenté par un répertoire, il est possible de le rendre exécutable en ajoutant un fichier __main__.py dans le répertoire. Ce fichier contient le code a exécuter uniquement si le module est lancé directement à partir de l’interpréteur Python grâce à l’option -m :

python3 -m mon_module

Chemin des modules

Comme un module est représenté par un fichier ou par un répertoire, l’interpréteur doit définir quel est l’emplacement par défaut des modules. L’ensemble des répertoires pouvant contenir des modules est appelé le path. Il est accessible directement par un programme avec sys.path.

import sys
print(sys.path)

# Affiche sur un système Linux
# ['', '/usr/lib/python36.zip', '/usr/lib/python3.6',
#  '/usr/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/dist-packages',
#  '/usr/lib/python3/dist-packages', '/usr/lib/python3.6/dist-packages']

Pour un système Linux, la variable sys.path indique tous les répertoires systèmes pouvant contenir des modules Python sous la forme d’un tableau. Le premier chemin indiqué est '', ce qui signifie que l’interpréteur cherche en priorité un module directement à partir du répertoire de travail dans lequel l’interpréteur a été lancé.

Il est possible de modifier le contenu de sys.path par programmation. On peut également utiliser la variable d’environnement système PYTHONPATH pour ajouter des chemins supplémentaires au moment du lancement de l’interpréteur. Si on souhaite spécifier plusieurs chemins, il faut les séparer par :.

$ export PYTHONPATH="/monrepertoire/python:/monrepertoire2"
$ python3
import sys
print(sys.path)

# Affiche sur un système Linux
# ['', '/monrepertoire/python', '/monrepertoire2', '/usr/lib/python36.zip',
#  '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload',
#  '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages',
#  '/usr/lib/python3.6/dist-packages']

Les packages

Les packages en Python désignent le fait de pouvoir structurer l’organisation des fichiers d’un projet dans une arborescence de répertoires. Par exemple si un projet a l’arborescence suivante :

stock/
        __init__.py
        ecran/
                __init__.py
                accueil.py
                detail_produit.py
        gestion
                __init__.py
                produit.py
                commande.py

Il est possible d’importer le module commande en utilisant son chemin de packages :

import stock.gestion.commande

stock.gestion.commande.traiter_commande()

Si on veut éviter de préciser systématiquement le chemin de packages pour accéder à élément du module, on peut utiliser la syntaxe du from ... import :

from stock.gestion import commande

commande.traiter_commande()

Note

La présence des fichiers __init__.py est obligatoire dans l’arborescence des packages pour que l’interpréteur Python traite chaque répertoire comme un module. Ces fichiers peuvent être vides.

Lorsque vous importez un module dans un chemin de package, le fichier __init__.py de chaque répertoire est appelé pour initialiser chacun des modules. Un module n’est initialisé qu’une fois ! Les imports successifs de ce module ou d’un sous module n’exécuteront plus son fichier __init__.py.

Import relatif

Dans une arborescence de packages, il est parfois plus simple d’utiliser un import relatif. Dans l’exemple d’arborescence précédent, supposons que dans le module stock.gestion.produit, on souhaite importer des fonctions présentes dans le fichier produit.py, on peut écrire :

Import depuis le fichier commande.py
import stock.gestion.produit

Cela a comme inconvénient de rendre le contenu du fichier dépendant de l’arborescence des packages. On peut également recourir à un import relatif. Puisque les fichiers commande.py et produit.py sont dans le même répertoire, on peut écrire :

Import relatif depuis le fichier commande.py
from . import produit

On peut également remonter dans l’arborescence en utilisant .. :

Import relatif depuis le fichier commande.py
from ..ecran import accueil

Exercices

Exercice en forme d’astuce

Essayez la commande :

python -m http.server

Exercice : Trouver un nombre

Utilisez la fonction randint() du module random pour tirer un nombre aléatoirement entre 1 et 20.

L’utilisateur a droit à trois tentatives pour deviner ce nombre.

Exercice : Afficher la date

Utilisez la fonction datetime.now() du module datetime pour connaître la date et l’heure et l’afficher.

Exercice : Organiser un projet en module

Reprenez le projet pour l’affichage de l’écart avec la taille. Découpez ce projet avec un module sous la forme d’un répertoire qui s’appelle taille.

Le contenu du module doit être :

taille
    __init__.py
    __main__.py
    entree_sortie.py
    calcul.py

Le module entree_sortie doit regrouper les fonctions pour interagir avec l’utilisateur. Le module calcul doit regrouper la fonction de calcul de différence. Le fichier principal du module __main__ doit permettre d’exécuter le programme.

L’application peut maintenant s’exécuter directement en ligne de commande en invoquant le module :

$ python -m taille