Avancez pas à pas vers la modularité (2/2)
Nous allons commencer par voir comment mettre nos programmes en boîte… ou plutôt en fichier.
Je vais faire d'une pierre deux coups : d'abord, c'est chouette d'avoir son programme dans un fichier modifiable à souhait,
surtout qu'on commence à pouvoir faire des programmes assez sympas (même si vous n'en avez peut-être pas l'impression).
Ensuite, c'est un prélude nécessaire à la création de modules.
Comme vous allez le voir, nos programmes Python peuvent être mis dans des fichiers pour être exécutés ultérieurement.
De ce fait, vous avez déjà pratiquement toutes les clés pour créer un programme Python exécutable.
Le même mécanisme est utilisé pour la création de modules.
Les modules sont eux aussi des fichiers contenant du code Python.
Enfin, nous verrons à la fin de ce chapitre comment créer des packages pour regrouper nos modules ayant un rapport entre eux.
Mettre en fichier notre code
Je viens pour conquérir le monde… et créer mes propres modules,
Mes modules à moi
Bon, nous avons vu le plus dur… ça va ? Rassurez-vous, nous n'allons rien faire de compliqué dans cette dernière section. Le plus dur est derrière nous.
Commencez par vous créer un espace de test pour les petits programmes Python que nous allons être amenés à créer,
un joli dossier à l'écart de vos photos et musiques. Peu importe son nom . Par exemple Ncours7
Nous allons créer deux fichiers portant l'extension .py dans ce dossier :
un fichier multipli.py, qui contiendra la fonction table() que nous avons codée au chapitre précédent ;
un fichier test.py, qui contiendra le test d'exécution de notre module.
Vous devriez vous en tirer sans problème. N'oubliez pas de spécifier la ligne précisant l'encodage en tête de vos deux fichiers.
Maintenant, voyons le code du fichier multipli.py.
"""module multipli contenant la fonction table"""
def table(nb, max=10) :
"""Fonction affichant la table de multiplication par nb de
1 * nb jusqu'à max * nb"""
i = 0
while i < max :
print(i + 1, "*", nb, "=", (i + 1) * nb)
i += 1
On se contente de définir une seule fonction, table() , qui affiche la table de multiplication choisie. Rien de nouveau jusqu'ici.
Si vous vous souvenez des docstrings, dont nous avons parlé au chapitre précédent, vous voyez que nous en avons inséré une nouvelle ici,
non pas pour commenter une fonction mais bien un module entier. C'est une bonne habitude à prendre quand nos projets deviennent importants.
Voici le code du fichier test.py, n'oubliez pas la ligne précisant votre encodage, en tête du fichier.
import os
from multipli import *
# test de la fonction table
table(3, 20)
os.system("pause")
En le lançant directement, voilà ce qu'on obtient :
1 * 3 = 3
2 * 3 = 6
3 * 3 = 9
4 * 3 = 12
5 * 3 = 15
6 * 3 = 18
7 * 3 = 21
8 * 3 = 24
9 * 3 = 27
10 * 3 = 30
11 * 3 = 33
12 * 3 = 36
13 * 3 = 39
14 * 3 = 42
15 * 3 = 45
16 * 3 = 48
17 * 3 = 51
18 * 3 = 54
19 * 3 = 57
20 * 3 = 60
Appuyez sur une touche pour continuer...
Je ne pense pas avoir grand chose à ajouter. Nous avons vu comment créer un module, il suffit de le mettre dans un fichier.
On peut alors l'importer depuis un autre fichier contenu dans le même répertoire en précisant le nom du fichier (sans l'extension.py).
Notre code, encore une fois, n'est pas très utile mais vous pouvez le modifier pour le rendre plus intéressant, vous en avez parfaitement les compétences à présent.
Au moment d'importer votre module, Python va lire (ou créer si il n'existe pas) un fichier .pyc. À partir de la version 3.2, ce fichier se trouve dans un dossier __pycache__.
Ce fichier est généré par Python et contient le code compilé (ou presque) de votre module.
Il ne s'agit pas réellement de langage machine mais d'un format que Python décode un peu plus vite que le code que vous pouvez écrire.
Python se charge lui-même de générer ce fichier et vous n'avez pas vraiment besoin de vous en soucier quand vous codez, simplement ne soyez pas surpris.
Faire un test de module dans le module-même
Dans l'exemple que nous venons de voir, nous avons créé deux fichiers, le premier contenant un module, le second testant ledit module.
Mais on peut très facilement tester le code d'un module dans le module même.
Cela veut dire que vous pourriez exécuter votre module comme un programme à lui tout seul, un programme qui testerait le module écrit dans le même fichier.
Voyons cela.
Reprenons le code du module multipli :
"""module multipli contenant la fonction table"""
def table(nb, max=10):
"""Fonction affichant la table de multiplication par nb de
1 * nb jusqu'à max * nb"""
i = 0
while i < max:
print(i + 1, "*", nb, "=", (i + 1) * nb)
i += 1
Ce module définit une seule fonction, table(), qu'il pourrait être bon de tester. Oui mais… si nous rajoutons juste en dessous une ligne, par exemple table(8),
cette ligne sera exécutée lors de l'importation et donc, dans le programme appelant le module. Quand vous ferez import multipli, vous verrez la table de multiplication par 8 s'afficher…
mais il y a encore mieux.
Heureusement, il y a un moyen très rapide de séparer les éléments du code qui doivent être exécutés lorsqu'on lance le module directement
en tant que programme ou lorsqu'on cherche à l'importer. Voici le code de la solution, les explications suivent :
(on a recopié multipli.py en multiply.py pour que ce soit plus clair )
"""module multiply contenant la fonction table"""
import os
def table(nb, max=10):
"""Fonction affichant la table de multiplication par nb de
1 * nb jusqu'à max * nb"""
i = 0
while i < max:
print(i + 1, "*", nb, "=", (i + 1) * nb)
i += 1
# test de la fonction table()
if __name__ == "__main__": # deux _ à la suite chaque fois
table(4)
os.system("pause")
N'oubliez pas la ligne indiquant l'encodage !
Voilà. À présent, si vous faites un double-clic directement sur le fichier multiply.py, vous allez voir la table de multiplication par 4.
En revanche, si vous l'importez, le code de test ne s'exécutera pas. Tout repose en fait sur la variable __name__,
c'est une variable qui existe dès le lancement de l'interpréteur. Si elle vaut __main__, cela veut dire que le fichier appelé est le fichier exécuté.
Autrement dit, si __name__ vaut __main__, vous pouvez mettre un code qui sera exécuté si le fichier est lancé directement comme un exécutable.
Prenez le temps de comprendre ce mécanisme, faites des tests si nécessaire, cela pourra vous être utile par la suite.
Les packages
Les modules sont un des moyens de regrouper plusieurs fonctions (et, comme on le verra plus tard, certaines classes également).
On peut aller encore au-delà en regroupant des modules dans ce qu'on va appeler des packages.
En théorie
Comme je l'ai dit, un package sert à regrouper plusieurs modules. Cela permet de ranger plus proprement vos modules,
classes et fonctions dans des emplacements séparés. Si vous voulez y accéder, vous allez devoir fournir un chemin vers le module que vous visez.
De ce fait, les risques de conflits de noms sont moins importants et surtout, tout est bien plus ordonné.
Par exemple, imaginons que vous installiez un jour une bibliothèque tierce pour écrire une interface graphique.
En s'installant, la bibliothèque ne va pas créer ses dizaines (voire ses centaines) de modules au même endroit.
Ce serait un peu désordonné… surtout quand on pense qu'on peut ranger tout cela d'une façon plus claire :
d'un côté, on peut avoir les différents objets graphiques de la fenêtre, de l'autres les différents évènements (clavier, souris,…), ailleurs encore les effets graphiques…
Dans ce cas, on va sûrement se retrouver face à un package portant le nom de la bibliothèque.
Dans ce package se trouveront probablement d'autres packages, un nommé evenements, un autre objets, un autre encore effets.
Dans chacun de ces packages, on pourra trouver soit d'autres packages, soit des modules et dans chacun de ces modules, des fonctions.
Ouf ! Cela nous fait une hiérarchie assez complexe non ? D'un autre côté, c'est tout l'intérêt.
Concrètement, pour utiliser cette bibliothèque, on n'est pas obligé de connaître tous ses packages, modules et fonctions (heureusement d'ailleurs !) ,
mais juste ceux dont on a réellement besoin.
En pratique
En pratique, les packages sont… des répertoires ! Dedans peuvent se trouver d'autres répertoires (d'autres packages) ou des fichiers (des modules).
Exemple de hiérarchie
Pour notre bibliothèque imaginaire, la hiérarchie des répertoires et fichiers ressemblerait à cela :
Un répertoire du nom de la bibliothèque contenant :
un répertoire evenements contenant :
un module clavier;
un module souris;
…
un répertoire effets contenant différents effets graphiques ;
un répertoire objets contenant les différents objets graphiques de notre fenêtre (boutons, zones de texte, barres de menus…).
Importer des packages
Si vous voulez utiliser, dans votre programme, la bibliothèque fictive que nous venons de voir, vous avez plusieurs moyens qui tournent tous autour des mots clés from et import :
import nom_bibliotheque
Cette ligne importe le package contenant la bibliothèque. Pour accéder aux sous-packages, vous utiliserez un point « . » afin de modéliser le chemin
menant au module ou à la fonction que vous voulez utiliser :
nom_bibliotheque.evenements # Pointe vers le sous-package evenements
nom_bibliotheque.evenements.clavier # Pointe vers le module clavier
Si vous ne voulez importer qu'un seul module (ou qu'une seule fonction) d'un package, vous utiliserez une syntaxe similaire, assez intuitive :
Exemple :
from nom_bibliotheque.objets import bouton
En fonction des besoins, vous pouvez décider d'importer tout un package, un sous-package, un sous-sous-package… ou bien
juste un module ou même une seule fonction. Cela dépendra de vos besoins.
Créer ses propres packages
Si vous voulez créer vos propres packages, commencez par créer, dans le même dossier que votre programme Python, un répertoire portant le nom du package.
Dans ce répertoire, vous pouvez soit :
mettre vos modules, vos fichiers à l'extension .py ;
créer des sous-packages de la même façon, en créant un répertoire dans votre package.
Ne mettez pas d'espaces dans vos noms de packages et évitez aussi les caractères spéciaux.
Quand vous les utilisez dans vos programmes, ces noms sont traités comme des noms de variables et ils doivent donc obéir aux mêmes règles de nommage.
Le fichier d'initialisation
En Python, vous trouverez souvent le fichier d'initialisation de package __init__.py dans un répertoire destiné à devenir un package.
Ce fichier est optionnel depuis la version 3.3 de Python. Vous n'êtes pas obligé de le créer mais vous pouvez y mettre du code d'initialisation pour votre package.
Je ne vais pas rentrer dans le détail ici (vous avez déjà beaucoup de choses à retenir), mais sachez que ce code d'initialisation est appelé quand vous importez votre package.
Un dernier exemple
Voici un dernier exemple, que vous pouvez cette fois faire en même temps que moi pour vous assurer que cela fonctionne.
Dans votre répertoire de code, là où vous mettez vos exemples Python, créez un fichier .py que vous appelerez test_package.py.
Créez dans le même répertoire un dossier package. Dedans, créez un fichier fonctions.py dans lequel vous recopierez votre fonction table().
Dans votre fichier test_package.py, si vous voulez importer votre fonction table(), vous avez plusieurs solutions :
from package.fonctions import table
table(5) # Appel de la fonction table
Ou ...
import package.fonctions
package.fonctions.table(5) # Appel de la fonction table
Voilà. Il reste bien des choses à dire sur les packages mais je crois que vous avez vu l'essentiel.
Cette petite explication révélera son importance quand vous aurez à construire des programmes assez volumineux.
Évitez de tout mettre dans un seul module sans chercher à hiérarchiser, profitez de cette possibilité offerte par Python.
En résumé
On peut écrire les programmes Python dans des fichiers portant l'extension .py.
On peut créer des fichiers contenant des modules pour séparer le code.
On peut créer des répertoires contenant des packages pour hiérarchiser un programme.