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.
Il y a de fait trois variantes d’une même syntaxe pour importer une bibliothèque extérieure.
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
help
pour accéder à la documentation pour sys
le code>>> 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.
...
dir
pour lister le contenu interne du module, pour le module sys
>>> import sys
>>> dir(sys)
['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '__unraisablehook__', '_base_executable', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework', '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook', 'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix', 'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth', 'getallocatedblocks', 'getcheckinterval', 'getdefaultencoding', 'getdlopenflags', 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix', 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info', 'warnoptions']
On peut bien sûr ensuite récupérer l’aide des objets internes via help
.
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'>
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
.py
par tous les import, et ce avant de coder quoi que ce soit d’autres.from BIBLIOTHEQUE import *
qui importe tous les objets de la bibliothèque dans l’espace global de noms est à proscrire. Même si elle semble très populaire sur internet, elle est totalement opaque et incontrôlable.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:
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.
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.
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'
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.
__doc__
, __file__
et __name__
.A quoi peut servir l’instruction composée suivante dans un module:
if __name__ == "__main__":
...
collections
, copy
, decimal
, math
, random
de la librairie standard? Donner des exemples d’utilisation.Installer le paquet rich
depuis PyPI. Donner un exemple d’utilisation.
>>> 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)
__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
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.
On peut importer ces modules utiliser dir
et help
et constater que.
Le module collections
contient des structures de données supplémentaires. Donnons un exemple de namedtuple
.
>>> import collections as cs
>>> Point = cs.namedtuple("Point", ["x", "y"])
>>> p = Point(x=1, y=2)
>>> p
Point(x=1, y=2)
>>> p[0] + p[1]
3
>>> p.x
1
>>> p.y
2
Ainsi on a crée un sous type de tuple
dont les différents champs sont nommés et accessibles par leur nom.
copy
permettait d’effectuer des copies d’objets mutables via la fonction deepcopy
Le module decimal
permet d’avoir des nombres à virgules flottantes avec un nombre de chiffre après la virgule arbitraire pour plus de précision.
>>> import decimal as dc
>>> dc.Decimal(1) / 3
Decimal('0.3333333333333333333333333333')
>>> dc.getcontext().prec = 1000
>>> dc.Decimal(1) / 3
Decimal('0.3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333')
Le module math
contient les fonctions mathématiques classiques.
>>> import math
>>> math.sqrt(2)
1.4142135623730951
>>> math.cos(math.pi / 6)
0.8660254037844387
>>> math.sqrt(3) / 2
0.8660254037844386
Le module random
permet d’avoir un générateur de nombres aléatoires pour différentes distributions.
>>> import random as rd
>>> lancers_de = [rd.randint(1, 6) for _ in range(10)]
>>> lancers_de
[3, 3, 6, 5, 6, 5, 3, 4, 3, 4]
>>> rd.gauss(mu=0, sigma=0.1)
-0.08527726897310657
>>> rd.gauss(mu=0, sigma=0.1)
0.10616039373821878
>>> rd.gauss(mu=0, sigma=0.1)
0.13208417916539986
>>> rd.gauss(mu=0, sigma=0.1)
-0.09392049814789508
>>> rd.gauss(mu=0, sigma=0.1)
0.00487012217968147
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 là 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!