Les modules

Les modules sont des bibliothèques de code python qui sont utilisables à la demande. On va montrer comment les utiliser et comment en créer.

Utilisation

Il y a de fait trois variantes d’une même syntaxe pour importer une bibliothèque extérieure.

  1. La plus fondamentale est import BIBLIOTHEQUE. Elle permet une fois l’instruction effectuée d’avoir un objet BIBLIOTHEQUE dans l’espace de noms global.
    On utilise alors l’opérateur . pour accéder au contenu interne. Donnons un exemple via la bibliothèque sys qui permet de gérer l’interpréteur python.

    >>> import sys
    >>> type(sys)
    <class 'module'>
    >>> print(sys.path)
    ['', '/home/vincent/miniconda3/lib/python38.zip', '/home/vincent/miniconda3/lib/python3.8', '/home/vincent/miniconda3/lib/python3.8/lib-dynload', '/home/vincent/.local/lib/python3.8/site-packages', '/home/vincent/miniconda3/lib/python3.8/site-packages']

    On verra plus tard dans cette section le sens et l’intérêt de la variable path.

Comme pour tout objet python, on peut utiliser les fonctions

>>> import sys
>>> help(sys)

provoque l’affichage (on a tronqué l’affichage qui est fleuve)


Help on built-in module sys:

NAME
    sys

MODULE REFERENCE
    https://docs.python.org/3.8/library/sys

    The following documentation is automatically generated from the Python
    source files.  It may be incomplete, incorrect or include features that
    are considered implementation detail and may vary between Python
    implementations.  When in doubt, consult the module reference at the
    location listed above.

DESCRIPTION
    This module provides access to some objects used or maintained by the
    interpreter and to functions that interact strongly with the interpreter.
    ...
  1. Le premier raffinement dans la syntaxe précédente s’utilise lorsque le nom de la bibliothèque est trop long.
    On peut alors utiliser import BIBLIOTHEQUE as RACCOURCI, le nom de l’objet correspondant à la bibliothèque est alors RACCOURCI. Ainsi par convention la bibliothèque numpy (pour numerical python) est toujours importée sous la forme np:

    >>> import numpy as np
    >>> np.linspace(0, 1, 10)
    array([0.        , 0.11111111, 0.22222222, 0.33333333, 0.44444444,
       0.55555556, 0.66666667, 0.77777778, 0.88888889, 1.        ])

    REMARQUE notez au passage qu’on peut avoir des sous bibliothèques à l’intérieur d’une bibliothèque. Là encore l’accès se fait grâce à l’opérateur .. Par exemple

    >>> import os.path as op
    >>> type(op)
    <class 'module'>
  2. Finalement, on peut directement injecter un ou plusieurs objets internes à la bibliothèque directement dans l’espace global de noms. On le fait en utilisant la syntaxe from BIBLIOTHEQUE import OBJET1, OBJET2, OBJET3. Par exemple,

    >>> from fractions import Fraction
    >>> moitie = Fraction(1, 2)
    >>> print(moitie)
    1/2
    >>> print(moitie + Fraction(1, 3))
    5/6

    REMARQUE l’objet Fraction permet de faire des calculs exacts sur les nombres rationnels.

ATTENTION cette syntaxe est à utiliser avec précaution car

REMARQUE on peut utiliser help("modules") pour lister toutes les bibliothèques accessibles depuis votre session python. (attention il peut y en avoir beaucoup).

Ces bibliothèques sont de trois types:

  1. les bibliothèques présentes dès l’installation de python. C’est ce qu’on appelle la librairie standard, elle contient déjà énormément de fonctionnalités.

  2. les bibliothèques de PyPI (Python Package Index). On peut les installer via la commande shell python -m install BIBLIOTHEQUE, comme si vous lanciez un script python. Parmi celles-ci on peut citer numpy, matplotlib, scipy et pandas qui sont les principales composantes du python scientific stack.
    Il y en a bien sûr beaucoup d’autres PyPI ayant en effet plus de 280000 librairies disponibles.

  3. les bibliothèques locales que vous avez crées vous même (voire la prochaine section)

La variable sys.path que l’on a vu au dessus est justement celle qui contrôle les endroits dans l’arborescence de fichiers (et l’ordre) où python va aller chercher les bibliothèques. Si jamais vous n’arrivez pas à importer une bibliothèque que vous pensez avoir installé, il faut contrôler que l’endroit est bien accessible via cette variable (potentiellement dans un sous répertoire).

A titre d’exemple dans mon cas

>>> import sys
>>> print(sys.path)
[
    '',
    '/home/vincent/miniconda3/lib/python38.zip',
    '/home/vincent/miniconda3/lib/python3.8',
    '/home/vincent/miniconda3/lib/python3.8/lib-dynload',
    '/home/vincent/.local/lib/python3.8/site-packages',
    '/home/vincent/miniconda3/lib/python3.8/site-packages',
    '/home/vincent/miniconda3/lib/python3.8/site-packages/IPython/extensions',
    '/home/vincent/miniconda3/lib/python3.8/site-packages/astroid/brain'
]

Notez que la str vide en premier signifie la localisation courante, celle depuis laquelle l’interpréteur a été lancée.
On peut utiliser le module os qui donne à python les accès aux routines du système d’exploitation pour connaître cette localisation.

>>> import os
>>> os.getcwd()
'/home/vincent'

Création

Tout fichier .py est un module potentiel que l’on peut importer. Ainsi si j’ai un fichier mon_module.py de contenu

#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""Description.

Module de démonstration.
"""

ma_variable = 1


def ma_fonction(argument):
    print("ça marche.")
    print(f"L'argument est {argument}")

et que je lance une session interactive à cet endroit précis (utiliser os.getcwd si vous voulez en être sûr) on peut faire

>>> import mon_module
>>> mon_module.ma_variable
1
>>> mon_module.ma_fonction("blabla")
ça marche.
L'argument est blabla
>>> dir(mon_module)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'ma_fonction', 'ma_variable']
>>> help(mon_module)

La dernière instruction affiche alors ceci

Help on module mon_module:

NAME
    mon_module - Description.

DESCRIPTION
    Module de démonstration.

FUNCTIONS
    ma_fonction(argument)

DATA
    ma_variable = 1

FILE
    /home/vincent/Code/python/mon_module.py

REMARQUE notez bien l’absence du .py dans le nom de module par rapport au nom du fichier.

ATTENTION notez bien que l’import du module génère l’exécution des toutes les instructions qu’il contient. Ainsi si j’ai un module test_import.py contenant le code

#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""Description.

Démonstration de l'exécution à l'import.
"""
import mon_module as mm

print("Cette instruction a bien été exécutée")

on constate durant la session

>>> import test_import
Cette instruction a bien été exécutée
>>> dir(test_import)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'mm']

REMARQUE notez bien la présence du module mon_module sous la forme de mm à l’intérieur de test_import.

En effet une librairie peut aussi être un répertoire contenant des modules et d’autres répertoires (etc…) Ainsi si les deux modules sont inclus dans un répertoire de nom demo_repertoire et que je lance l’interpréteur à côté du répertoire.

>>> import demo_repertoire as dr
>>> dir(dr)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']
>>> dr.__package__
'demo_repertoire'
>>> dr.__name__
'demo_repertoire'

on constate que les deux sous-modules n’ont pas été récupérés. On aurait pu par contre faire

>>> import demo_repertoire.mon_module as mm
>>> dir(mm)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'ma_fonction', 'ma_variable']

Mais la vraie solution consiste à créer dans demo_repertoire un fichier de nom __init__.py (le nom n’est pas négociable ici) qui sera exécuté à l’import. Si son contenu est par exemple:

from . import mon_module

print("On exécute bien __init__.py")

alors pendant la session interactive

>>> import demo_repertoire
On exécute bien __init__.py
>>> dir(demo_repertoire)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'mon_module']

REMARQUE on a juste donner une idée de la façon dont les choses se passent pour un répertoire. Regarder Pour aller plus loin pour en savoir plus.

ATTENTION Lorsqu’on développe un module, il n’est pas rare d’avoir une session interactive en parallèle pour tester son code. Il faut faire attention à la chose suivante. Si on effectue une deuxième fois l’instruction import du même module, alors même si celui a changé il n’y a pas de rechargement. Pour forcer cette recharge il faut utiliser la fonction reload du module importlib qu’on appelle avec l’objet représentant le module comme argument.

Exercices

  1. Dans un module à quoi correspondent les variables __doc__, __file__ et __name__.
  2. A quoi peut servir l’instruction composée suivante dans un module:

    if __name__ == "__main__":
    ...
  3. A quoi servent les modules collections, copy, decimal, math, random de la librairie standard? Donner des exemples d’utilisation.
  4. Installer le paquet rich depuis PyPI. Donner un exemple d’utilisation.

Pour aller plus loin

>>> help("IMPORTING")
>>> help("MODULES")
>>> help("PACKAGES")

On pourra consulter le livre de Doug Hellman: The python 3 standard library (ou le blog correspondant) pour des détails et des exemples sur tous les modules de la bibliothèque standard de python. (les modules présents dès l’installation de python)

Corrections

  1. Dans un module la variable __doc__: str contient en fait la docstring du module. Pour mon_module qu’on a vu plus haut il s’agissait de
>>> import mon_module as mm
>>> print(mm.__doc__)
Description.

Module de démonstration.

C’est une partie de ce que l’on récupère quand on appelle help sur le module.

La variable __file__: str contient elle le chemin complet du fichier contenant le module.

>>> import mon_module as mm
>>> print(mm.__file__)
/home/vincent/Code/python/demo_repertoire/mon_module.py

Finalement, la variable __name__: str contient le nom du module (pas le raccourci!)

>>> import mon_module as mm
>>> print(mm.__name__)
mon_module
  1. Pour comprendre l’intérêt de cette instruction il faut d’abord constater

    >>> print(__name__)
    __main__

    la variable global __name__ est peuplée au lancement de l’interpréteur par le nom __main__. L’instruction composée if __name__ == "__main__" permet donc de détecter si le module est exécuté directement par python, ou via un import depuis un autre module. On peut donc faire exécuter des instructions dans le module, uniquement si celui-ci est directement invoqué par python.

  2. On peut importer ces modules utiliser dir et help et constater que.

  1. On peut normalement installer le module rich via l’instruction python -m pip install rich. Cette bibliothèque permet de générer des affichages beaucoup plus riches.

    >>> print(locals())
    {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
    >>> import sys
    >>> print(sys.path)
    ['', '/home/vincent/miniconda3/lib/python38.zip', '/home/vincent/miniconda3/lib/python3.8', '/home/vincent/miniconda3/lib/python3.8/lib-dynload', '/home/vincent/.local/lib/python3.8/site-packages', '/home/vincent/miniconda3/lib/python3.8/site-packages']
    >>> from rich import print
    >>> print(locals())
    {
    '__name__': '__main__',
    '__doc__': None,
    '__package__': None,
    '__loader__': <class '_frozen_importlib.BuiltinImporter'>,
    '__spec__': None,
    '__annotations__': {},
    '__builtins__': <module 'builtins' (built-in)>,
    'sys': <module 'sys' (built-in)>,
    'print': <function print at 0x7f1c7aa433a0>
    }
    >>> print(sys.path)
    [
    '',
    '/home/vincent/miniconda3/lib/python38.zip',
    '/home/vincent/miniconda3/lib/python3.8',
    '/home/vincent/miniconda3/lib/python3.8/lib-dynload',
    '/home/vincent/.local/lib/python3.8/site-packages',
    '/home/vincent/miniconda3/lib/python3.8/site-packages'
    ]

    Notez qu’on a en vrai et en plus de la forme, une coloration syntaxique du résultat. On pourra aller voir pour des démonstrations beaucoup plus impressionnantes.

REMARQUE notez bien que l’utilisation de librairie externes complique l’utilisation de vos scripts sur d’autres machines!