6. Modules

Lorsque vous quittez et entrez à nouveau dans l’interpréteur Python, tout ce que vous avez déclaré dans la session précédente est perdu. Afin de rédiger des programmes plus longs, vous devez utiliser un éditeur de texte, préparer votre code dans un fichier et exécuter Python avec ce fichier en paramètre. Cela s’appelle créer un script. Lorsque votre programme grandit, vous pouvez séparer votre code dans plusieurs fichiers. Ainsi, il vous est facile de réutiliser des fonctions écrites pour un programme dans un autre sans avoir à les copier.

Pour gérer cela, Python vous permet de placer des définitions dans un fichier et de les utiliser dans un script ou une session interactive. Un tel fichier est appelé un module et les définitions d’un module peuvent être importées dans un autre module ou dans le module main (qui est le module qui contient vos variables et définitions lors de l’exécution d’un script au niveau le plus haut ou en mode interactif).

Un module est un fichier contenant des définitions et des instructions. Son nom de fichier est le nom du module suffixé de .py. À l’intérieur d’un module, son propre nom est accessible par la variable __name__. Par exemple, prenez votre éditeur favori et créez un fichier fibo.py dans le répertoire courant qui contient :

# Fibonacci numbers module

def fib(n):    # write Fibonacci series up to n
    a, b = 0, 1
    while b < n:
        print b,
        a, b = b, a+b

def fib2(n):   # return Fibonacci series up to n
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)
        a, b = b, a+b
    return result

Maintenant, ouvrez un interpréteur et importez le module en tapant :

>>> import fibo

Cela n’importe pas les noms des fonctions définies dans fibo directement dans la table des symboles courants mais y ajoute simplement fibo. Vous pouvez donc appeler les fonctions via le nom du module :

>>> fibo.fib(1000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> fibo.__name__
'fibo'

Si vous avez l’intention d’utiliser souvent une fonction, il est possible de lui assigner un nom local :

>>> fib = fibo.fib
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

6.1. Les modules en détail

Un module peut contenir aussi bien des instructions que des déclarations de fonctions. Ces instructions permettent d’initialiser le module. Elles ne sont exécutées que la première fois que le nom d’un module est trouvé dans un import [1] (elles sont aussi exécutées lorsque le fichier est exécuté en tant que script).

Chaque module possède sa propre table de symboles, utilisée comme table de symboles globaux par toutes les fonctions définies par le module. Ainsi l’auteur d’un module peut utiliser des variables globales dans un module sans se soucier de collisions de noms avec des variables globales définies par l’utilisateur du module. Cependant, si vous savez ce que vous faites, vous pouvez modifier une variable globale d’un module avec la même notation que pour accéder aux fonctions : nommodule.nomelement.

Des modules peuvent importer d’autres modules. Il est courant, mais pas obligatoire, de ranger tous les import au début du module (ou du script). Les noms des modules importés sont insérés dans la table des symboles globaux du module qui importe.

Il existe une variante de l’instruction import qui importe les noms d’un module directement dans la table de symboles du module qui l’importe, par exemple :

>>> from fibo import fib, fib2
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

Cela n’insère pas le nom du module depuis lequel les définitions sont récupérées dans la table des symboles locaux (dans cet exemple, fibo n’est pas défini).

Il existe même une variante permettant d’importer tous les noms qu’un module définit :

>>> from fibo import *
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

Importe tous les noms sauf ceux commençant par un tiret bas (_).

Notez qu’en général, importer * d’un module ou d’un paquet est déconseillé. Souvent, le code devient difficilement lisible. Son utilisation en mode interactif est acceptée pour gagner quelques secondes.

If the module name is followed by as, then the name following as is bound directly to the imported module.

>>> import fibo as fib
>>> fib.fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

This is effectively importing the module in the same way that import fibo will do, with the only difference of it being available as fib.

It can also be used when utilising from with similar effects:

>>> from fibo import fib as fibonacci
>>> fibonacci(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Note

Pour des raisons d’efficience, chaque module n’est importé qu’une fois par session de l’interpète. Si vous changez vos modules, vous devrez donc redémarrer votre interprète, ou, si vous souhaiter simplement tester un seul module de manière interactive, utiliser la fonction reload(), tel que: reload(modulename).

6.1.1. Exécuter des modules comme des scripts

Lorsque vous exécutez un module Python avec :

python fibo.py <arguments>

le code du module est exécuté comme si vous l’aviez importé mais son __name__ vaut "__main__". Donc, en ajoutant ces lignes à la fin du module :

if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))

vous pouvez rendre le fichier utilisable comme script aussi bien que comme module importable, car le code qui analyse la ligne de commande n’est lancé que si le module est exécuté comme fichier « main » :

$ python fibo.py 50
1 1 2 3 5 8 13 21 34

Si le fichier est importé, le code n’est pas exécuté :

>>> import fibo
>>>

C’est typiquement utilisé soit pour proposer une interface utilisateur pour un module, soit pour lancer les tests sur le module (exécuter le module en tant que script lance les tests).

6.1.2. Les dossiers de recherche de modules

Lorsqu’un module nommé par exemple spam est importé, il est d’abord recherché parmi les modules natifs puis, s’il n’est pas trouvé, l’interpréteur cherche un fichier nommé spam.py dans une liste de dossiers donnée par la variable sys.path. Par défaut, sys.path est initialisée à :

  • le dossier contenant le script d’entrée (ou le dossier courant).
  • PYTHONPATH (une liste de dossiers, utilisant la même syntaxe que la variable shell PATH) ;
  • les valeurs par défaut dépendantes de l’installation.

Après leur initialisation, les programmes Python peuvent modifier leur sys.path. Le dossier contenant le script courant est placé au début de la liste des dossiers à rechercher, avant les dossiers de bibliothèques. Cela signifie qu’un module dans ce dossier, ayant le même nom qu’un module, sera chargé à sa place. C’est une erreur typique, à moins que ce ne soit voulu. Voir Modules standards pour plus d’informations.

6.1.3. Fichiers Python « compilés »

Un gain de temps important au démarrage pour les courts programmes utilisant beaucoup de modules standards, si un fichier spam.pyc existe dans le dossier où spam.py`est trouvé, il est supposé contenir une version compilée du module :mod:`spam. La date de modification du fichier spam.py est utilisée pour créer le fichier spam.pyc, et stockée dans le fichier spam.pyc, ainsi le fichier .pyc est ignoré si ces deux dates ne coïncident pas.

Normalement, vous n’avez rien à faire pour créer un fichier spam.pyc. Lorsque le fichier spam.py est compilé, Python essaye d’écrire la version compilée dans spam.pyc. Ce n’est pas une erreur si cette tentative échoue, et si pour n’importe quelle raison, le fichier .pyc viendrait à ne pas être écrit entièrement, il serait reconnu comme invalide et ignoré. Le contenu du fichier spam.pyc est indépendant de la plateforme, donc un dossier contenant des modules Python peut être partagé entre différentes machines de différentes architectures.

Astuces pour les experts :

  • Lorsque l’interpréteur Python est invoqué avec l’option -O, un code optimisé est généré et stocké dans des fichiers en .pyo. L’optimiseur, aujourd’hui, n’aide pas beaucoup, il ne fait que supprimer les instructions assert. Lorsque l’option -O est utilisée, tous les bytecodes sont optimisés, les fichiers .pyc sont ignorés, et les fichier .py compilés en un bytecode optimisé.

  • Donner deux -O à l’interpréteur (-OO) engendrera un bytecode qui pourrait dans de rare cas provoquer un dysfonctionnement du programme. Actuellement seulement les chaînes __doc__ sont retirés du bytecode, rendant les .pyo plus petits. Puisque certains programmes peuvent en avoir besoin, vous ne devriez utiliser cette option que si vous savez ce que vous faites.

  • Un programme ne s’exécute pas plus vite lorsqu’il est lu depuis un .pyc ou .pyo, la seule chose qui est plus rapide est la vitesse à laquelle ils sont chargés.

  • Lorsqu’un programme est lancé en donnant son nom à une invite de commande, le bytecode du script n’est jamais écrit dans un .pyc ni .pyo. Cependant son temps de chargement peut être réduit en déplaçant la plupart de son code vers un module, et n’utiliser qu’un script léger pour importer ce module. Il est aussi possible d’exécuter un fichier .pyc ou .pyo directement depuis l’invite de commande.

  • Il est possible d’avoir un fichier spam.pyc (ou spam.pyo lorsque -O a été utilisée) sans spam.py pour un même module. Cela permet de distribuer une bibliothèque Python dans un format qui est modérément compliqué à rétro-ingénierer.

  • Le module compileall peut créer des fichiers .pyc (ou .pyo si l’option -O est fournie) pour chaque module d’un dossier.

6.2. Modules standards

Python est accompagné d’une bibliothèque de modules standards, décrits dans la documentation de la Bibliothèque Python, plus loin. Certains modules sont intégrés dans l’interpréteur, ils exposent des outils qui ne font pas partie du langage mais qui font tout de même partie de l’interpréteur, soit pour le côté pratique, soit pour exposer des outils essentiels tels que l’accès aux appels système. La composition de ces modules est configurable à la compilation et dépend aussi de la plateforme cible. Par exemple, le module winreg n’est proposé que sur les systèmes Windows. Un module mérite une attention particulière, le module sys, qui est présent dans tous les interpréteurs Python. Les variables sys.ps1 et sys.ps2 définissent les chaînes d’invites principales et secondaires :

>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print 'Yuck!'
Yuck!
C>

Ces deux variables ne sont définies que si l’interpréteur est en mode interactif.

La variable sys.path est une liste de chaînes qui détermine les chemins de recherche de modules pour l’interpréteur. Elle est initialisée à un chemin par défaut pris de la variable d’environnement PYTHONPATH ou d’une valeur par défaut interne si PYTHONPATH n’est pas définie. sys.path est modifiable en utilisant les opérations habituelles des listes :

>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')

6.3. La fonction dir()

La fonction interne dir() est utilisée pour trouver quels noms sont définis par un module. Elle donne une liste de chaînes classées par ordre lexicographique :

>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)  
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__package__',
 '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache',
 '_current_frames', '_getframe', '_mercurial', 'api_version', 'argv',
 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats',
 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_clear', 'exc_info',
 'exc_traceback', 'exc_type', 'exc_value', 'excepthook', 'exec_prefix',
 'executable', 'exit', 'flags', 'float_info', 'float_repr_style',
 'getcheckinterval', 'getdefaultencoding', 'getdlopenflags',
 'getfilesystemencoding', 'getobjects', 'getprofile', 'getrecursionlimit',
 'getrefcount', 'getsizeof', 'gettotalrefcount', 'gettrace', 'hexversion',
 'long_info', 'maxint', 'maxsize', 'maxunicode', 'meta_path', 'modules',
 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1',
 'py3kwarning', 'setcheckinterval', 'setdlopenflags', 'setprofile',
 'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout', 'subversion',
 'version', 'version_info', 'warnoptions']

Sans paramètre, dir() liste les noms actuellement définis :

>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir()
['__builtins__', '__name__', '__package__', 'a', 'fib', 'fibo', 'sys']

Notez qu’elle liste tous les types de noms : les variables, fonctions, modules, etc.

La fonction dir() ne liste pas les noms des fonctions et variables natives. Si vous en voulez la liste, ils sont définis dans le module standard __builtin__:

>>> import __builtin__
>>> dir(__builtin__)  
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException',
 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError',
 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError',
 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning',
 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt',
 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented',
 'NotImplementedError', 'OSError', 'OverflowError',
 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError',
 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError',
 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True',
 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError',
 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError',
 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning',
 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__',
 '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring',
 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr',
 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright',
 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval',
 'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset',
 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input',
 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license',
 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next',
 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit',
 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round',
 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super',
 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']

6.4. Les paquets

Packages are a way of structuring Python’s module namespace by using « dotted module names ». For example, the module name A.B designates a submodule named B in a package named A. Just like the use of modules saves the authors of different modules from having to worry about each other’s global variable names, the use of dotted module names saves the authors of multi-module packages like NumPy or Pillow from having to worry about each other’s module names.

Imaginez que vous voulez construire un ensemble de modules (un « paquet ») pour gérer uniformément les fichiers contenant du son et des données sonores. Il existe un grand nombre de formats de fichiers pour stocker du son (généralement identifiés par leur extension, par exemple .wav, .aiff, .au), vous avez donc besoin de créer et maintenir un nombre croissant de modules pour gérer la conversion entre tous ces formats. Vous voulez aussi pouvoir appliquer un certain nombre d’opérations sur ces sons : mixer, ajouter de l’écho, égaliser, ajouter un effet stéréo artificiel, etc. Donc, en plus des modules de conversion, vous allez écrire une myriade de modules permettant d’effectuer ces opérations. Voici une structure possible pour votre paquet (exprimée sous la forme d’une arborescence de fichiers :

sound/                          Top-level package
      __init__.py               Initialize the sound package
      formats/                  Subpackage for file format conversions
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Subpackage for sound effects
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Subpackage for filters
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

Lorsqu’il importe des paquets, Python cherche dans chaque dossier de sys.path un sous-dossier du nom du paquet.

Les fichiers __init__.py sont nécessaires pour que Python considère les dossiers comme contenant des paquets, cela évite que des dossiers ayant des noms courants comme string ne masquent des modules qui auraient été trouvés plus tard dans la recherche des dossiers. Dans le plus simple des cas, __init__.py peut être vide, mais il peut aussi exécuter du code d’initialisation pour son paquet ou configurer la variable __all__ (documentée plus loin).

Les utilisateurs d’un module peuvent importer ses modules individuellement, par exemple :

import sound.effects.echo

charge le sous-module sound.effects.echo. Il doit alors être référencé par son nom complet.

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

Une autre manière d’importer des sous-modules est :

from sound.effects import echo

charge aussi le sous-module echo et le rend disponible sans avoir à indiquer le préfixe du paquet. Il peut donc être utilisé comme ceci :

echo.echofilter(input, output, delay=0.7, atten=4)

Une autre méthode consiste à importer la fonction ou la variable désirée directement :

from sound.effects.echo import echofilter

Le sous-module echo est toujours chargé mais ici la fonction echofilter() est disponible directement :

echofilter(input, output, delay=0.7, atten=4)

Notez que lorsque vous utilisez from package import element, element peut aussi bien être un sous-module, un sous-paquet ou simplement un nom déclaré dans le paquet (une variable, une fonction ou une classe). L’instruction import cherche en premier si element est défini dans le paquet ; s’il ne l’est pas, elle cherche à charger un module et, si elle n’en trouve pas, une exception ImportError est levée.

Au contraire, en utilisant la syntaxe import element.souselement.soussouselement, chaque element sauf le dernier doit être un paquet. Le dernier element peut être un module ou un paquet, mais ne peut être ni une fonction, ni une classe, ni une variable définie dans l’élément précédent.

6.4.1. Importer * depuis un paquet

Qu’arrive-il lorsqu’un utilisateur écrit from sound.effects import * ? Idéalement, on pourrait espérer que Python aille chercher tous les sous-modules du paquet sur le système de fichiers et qu’ils seraient tous importés. Cela pourrait être long et importer certains sous-modules pourrait avoir des effets secondaires indésirables ou, du moins, désirés seulement lorsque le sous-module est importé explicitement.

La seule solution, pour l’auteur du paquet, est de fournir un index explicite du contenu du paquet. L’instruction import utilise la convention suivante : si le fichier __init__.py du paquet définit une liste nommée __all__, cette liste est utilisée comme liste des noms de modules devant être importés lorsque from package import * est utilisé. Il est de la responsabilité de l’auteur du paquet de maintenir cette liste à jour lorsque de nouvelles versions du paquet sont publiées. Un auteur de paquet peut aussi décider de ne pas autoriser d’importer * pour son paquet. Par exemple, le fichier sound/effects/__init__.py peut contenir le code suivant :

__all__ = ["echo", "surround", "reverse"]

Cela signifie que from sound.effects import * importe les trois sous-modules explicitement désignés du paquet sound.

Si __all__ n’est pas définie, l’instruction from sound.effects import * n’importe pas tous les sous-modules du paquet sound.effects dans l’espace de noms courant mais s’assure seulement que le paquet sound.effects a été importé (i.e. que tout le code du fichier __init__.py a été exécuté) et importe ensuite les noms définis dans le paquet. Cela inclut tous les noms définis (et sous-modules chargés explicitement) par __init__.py. Sont aussi inclus tous les sous-modules du paquet ayant été chargés explicitement par une instruction import. Typiquement :

import sound.effects.echo
import sound.effects.surround
from sound.effects import *

Dans cet exemple, les modules echo et surround sont importés dans l’espace de noms courant lorsque from...import est exécuté parce qu’ils sont définis dans le paquet sound.effects (cela fonctionne aussi lorsque __all__ est définie).

Although certain modules are designed to export only names that follow certain patterns when you use import *, it is still considered bad practice in production code.

Rappelez-vous que rien ne vous empêche d’utiliser from paquet import sous_module_specifique ! C’est d’ailleurs la manière recommandée, à moins que le module qui fait les imports ait besoin de sous-modules ayant le même nom mais provenant de paquets différents.

6.4.2. Références internes dans un paquet

Les sous modules ont souvent besoin de s’utiliser entre eux. Par exemple le module surround pourrait utiliser le module echo. Dans les faits ces références sont si communes que l’instruction clef import regarde d’abord dans le paquet avant de chercher dans les modules standard. Donc le module surround peut simplement utiliser import echo ou from echo import echofilter. Si le module n’est pas trouvé dans le paquet courrant (le paquet pour lequel le module actuel est un sous module), l’instruction import cherchera le nom donné en dehors du paquet.

Lorsque les paquets sont organisés en sous-paquets (comme le paquet sound par exemple), vous pouvez utiliser des imports absolus pour cibler des paquets voisins. Par exemple, si le module sound.filters.vocoder a besoin du module echo du paquet sound.effects, il peut utiliser from sound.effects import echo.

Depuis Python 2.5, en plus des imports relatifs décris plus haut, vous pouvez écrire des imports relatifs explicites via la syntaxe form module import name. Ces imports relatifs explicits utilisent le préfixe point (.) pour indiquer le paquet courant ou le paquet parent. Pour le module surround par exemple vous pourriez utiliser :

from . import echo
from .. import formats
from ..filters import equalizer

Notez que les imports relatifs, implicites et explicites, sont basés sur le nom du module courant. Puisque le nom du module principal est toujours "__main__", les modules conçus comme modules principaux d’une application doivent toujours utiliser des imports absolus.

6.4.3. Paquets dans plusieurs dossiers

Les paquets possèdent un attribut supplémentaire, __path__, qui est une liste initialisée avant l’exécution du fichier __init__.py, contenant le nom de son dossier dans le système de fichiers. Cette liste peut être modifiée, altérant ainsi les futures recherches de modules et sous-paquets contenus dans le paquet.

Bien que cette fonctionnalité ne soit que rarement utile, elle peut servir à élargir la liste des modules trouvés dans un paquet.

Notes

[1]En réalité, la déclaration d’une fonction est elle-même une instruction ; son exécution enregistre le nom de la fonction dans la table des symboles globaux du module.