3.10. Modules

3.10.1. Introduction

L’utilisation interactive de Python n’est pas très pratique, car elle ne permet pas de sauvegarder les variables et autres résultats. Pour y remédier, l’on écrit le code dans des fichiers [1], appelés modules (ou scripts), qui permettent de réutiliser le code. De même, il est fortement conseillé de diviser un long programme en de nombreux modules pour plus de clarté.

Un module peut contenir aussi bien des définitions (de classe ou de fonctions) que des expressions, mais ces dernières ne seront exécutées qu’une seule fois (la première fois que le module est importé).

Les modules peuvent être nommés de la même manière que les variables. Un module est importé grâce au mot-clé import suivi du nom d’un ou plusieurs modules [2] :

import mod1, ..., modn

Il est alors possible d’accéder aux éléments qu’il contient grâce au point (puisqu’un module est considéré comme un objet). Tout module peut être importé dans un autre module, dans le module principal ou dans l’interpréteur interactif.

Enfin, au même titre que pour les fonctions et le classes, un module peut comporter une docstring.

Créons par exemple un fichier fibo.py contenant du code :

''' This module contains the Fibonacci series.
'''

__all__ = ['fibo_func']

def fibo_func(n):
    a, b = 0, 1
    while b < n:
        yield b
        a, b = b, a + b

func_type = "generator"

if __name__ == '__main__':
    print(list(fibo_func(6)))

Maintenant, lançons l’interpréteur interactif :

>>> import fibo
>>> print(fibo.func_type)
generator
>>> list(fibo.fibo_func(4))
[1, 1, 2, 3]

Il est possible de définir des alias [3] :

>>> fib = fibo.fibo_func
>>> list(fib(4))
[1, 1, 2, 3]

Dans chaque module importé est définie la variable __name__, qui contient le nom du module :

>>> fibo.__name__
'fibo'

Terminons cette section en notant qu’un module n’est importé qu’une seule fois. Les modifications apportées après l’import ne seront donc pas prises en compte, à moins que l’on redémarre l’interpréteur ou que l’on relance le programme.

Footnotes

[1]Par convention, ces fichiers portent l’extension .py.
[2]Il est commun de placer tous les imports au début du fichier (mais après la docstring), même si ce n’est pas obligé.
[3]Ceci n’est pas la manière habituelle de procéder. Nous verrons par la suite comment créer un véritable alias.

3.10.2. Variantes

L’on peut choisir d’importer certaines éléments d’un module directement dans le contexte global, en utilisant from-import. L’on peut attribuer un alias à un élément apporté à l’aide du mot-clé as. Il est possible de mixer les éléments avec alias et sans alias.

from module import name1 [as alias1], ..., namen [as aliasn]

Dans ce cas là, il convient de noter que le module lui-même n’est pas importé.

>>> from fibo import fibo_func, func_type as type
>>> list(fibo_func(4))
[1, 1, 2, 3]
>>> type
'generator'
>>> fibo 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'fibo' is not defined
>>> from fibo import fibo_func as fib
>>> list(fib(4))
[1, 1, 2, 3]

L’étoile * permet d’importer tout un ensemble d’éléments, définis dans une liste nommée __all__.

Reprenons le fichier précédent et ajoutons au début de ce dernier :

__all__ = ['fibo_func']

>>> from fibo import *
>>> list(fibo_func(4))
[1, 1, 2, 3]

Toutefois, cette syntaxe n’est pas autorisée en dehors du niveau du module (par exemple elle ne peut être utilisée dans la définition d’une fonction).

3.10.3. Scripts

Pour utiliser un module comme un script, il suffit de fournir, dans un shell, le nom du module en argument :

$ python fibo.py

Dans ce cas uniquement, la variable __name__ vaut __main__. Ceci sert généralement à introduire, en fin de module, du code qui ne doit être exécuté que si le module est utilisé comme un script. Par exemple, nous pourrions ajouter à la fin de notre fichier fibo.py :

if __name__ == '__main__':
    print(list(fibo_func(6)))

Le résultat de sera évidemment [1, 1, 2, 3, 5].

3.10.4. Mécanismes

3.10.4.1. Module path

Lorsque l’on importe un module, ce dernier est recherché dans les dossiers contenus dans la variable sys.path (une liste, que l’on peut modifier). Cette dernière contient les mêmes éléments que la variable shell PYTHONPATH (dont la syntaxe est la même que pour PATH) ainsi que les éventuels répertoires ajoutés.

Ainsi, si l’un de vos modules ne veut pas être importé, vérifiez s’il est bien dans le path.

Warning

Un module ne devrait pas être nommé de la même manière qu’un module standard.

3.10.4.2. Fichiers compilés

L’interpréteur Python crée, lorsqu’il importe un module (nommé name.py pour l’exemple), un fichier nommé name.pyc. Il s’agit d’une version compilée, et donc plus rapide, du fichier en question.

Ainsi, lorsqu’il existe à la fois une version .py et une version .pyc, l’interpréteur vérifie si le code contenu dans le .py n’a pas changé depuis la dernière fois qu’il a été importé ; le cas échéant, l’interpréteur utilisera la version compilée. Par contre, si le code a changé, alors l’interpréteur chargera la version .py et créera un nouvel .pyc, pour un usage ultérieur.

Note

Il est aussi possible de générer du bytecode optimité (l’extension des fichiers sera alors .pyo). Ceci sera vu en détails plus tard.

3.10.4.3. Mise en garde sur l’import

Lorsqu’un module est importé, une référence est stockée dans le dictionnaire sys.modules.

Par exemple, au lancement de l’interpréteur interactif, j’obtiens :

>>> from sys import modules
>>> modules 
{'__main__': <module '__main__' (built-in)>, 'site': <module 'site' from '/usr/local/lib/python3.1/site.py'>, 'io': <module 'io' from '/usr/local/lib/python3.1/io.py'>, ...}

Cela signifie qu’un module n’est importé qu’une fois : les fois suivantes, Python ne fait rien. Ainsi, il évite d’importer plusieurs fois des modules ce qui permet d’améliorer les performances. Toutefois, cela est peu pratique lorsque l’on veut tester un module, car il est nécessaire de quitter l’interpréteur et de le relancer si on édite le fichier. Pour remédier à ce problème, on pourra se tourner vers l’interpréteur IPython.

3.10.5. Modules standards

Il existe de nombreux modules distribués par défaut avec Python, dont il serait trop long de faire la liste. Pour cette raison, la liste qui suit contient uniquement les principaux modules.

  • sys : de nombreuses informations sur le système ;
  • os : fonctions pour interragir avec le système d’exploitation ;
  • os.path : fonctions pour manipuler fichiers et dossiers ;
  • glob: ce module contient une fonction pour créer une liste de fichiers au moyen de jokers (*) ;
  • re : utilisation des expressions régulières ;
  • math : fonctions et constantes mathématiques (sin, cos, exp, e, pi, etc) ;
  • random : génération de nombres aléatoires ;
  • datetime : utilisation de dates.

Il ne s’agit que d’un très bref aperçu, et il est conseillé au lecteur de se renseigner dans la documentation dès qu’il a besoin d’une certaine fonction.

3.10.6. Packages

Un package est un ensemble de plusieurs modules. Il s’agit donc d’un dossier qui, pour que Python sache qu’il s’agit d’un package, doit contenir un fichier __init__.py [4].

L’on accède au module d’un package en utilisant le point, et il est possible de créer autant de sous-packages que l’on souhaite. Nous pourrions donc avoir une arborescence de ce type :

sound/
    __init__.py
    formats/
        __init__.py
        wavread.py
        wavwrite.py
        ...
    effects/
        __init__.py
        echo.py
        surround.py
        ...
    filters/
        __init__.py
        equalizer.py
        vocoder.py

Nous pouvons alors importer tout un package, un ou plusieurs modules individuellement.

>>> import sound.filters.echo
>>> # access at the elements with "sound.filters.echo"
>>> from sound.filters import echo
>>> # access at the elements with "echo"
>>> from sound.effects.echo import echofilter
>>> # access at the function "echofilter"

Footnotes

[4]Ce fichier est généralement vide, mais il peut contenir du code qui sera exécuté lors de l’import du package, comme par exemple définir la variable __all__.

3.10.7. Imports relatif et absolus

Il est possible d’accéder à un module aussi bien grâce au chemin absolu, ou bien grâce au chemin relatif si ce dernier se trouve dans le même dossier (ou donc dans un sous-dossier).

Par exemple, si l’on reprend la liste de fichiers précédentes, dans le fichier echo.py, nous pourrions importer le module surround grâce à l’une des deux syntaxes suivantes :

import sound.effects.surround
import surround