LangageModules

[wiki: Modules] [#license ©]

PageOutline

Définition d’un module

  • Tout programme python peut-être découpé en plusieurs fichiers.
  • L’importation d’un fichier occasionne la création d’un objet de type module, qui recevra toutes les variables et fonctions définies au sein du fichier.
  • Instructions ou fonction prédéfinies permettant d’importer un fichier :
  • import (récupération d’un fichier complet)
  • from (récupération de quelques éléments)
  • reload (force le rechargement)

Premier import

Effet de la première instruction import mod d’un programme :
1. Recherche du fichier mod.py dans les répertoires de la liste sys.path :
1. répertoire courant
1. répertoires de PYTHONPATH
1. répertoires standards
1. Compilation, si mod.pyc n’est pas à jour.
1. Exécution de mod.pyc, en enregistrant variables et fonctions dans le module ainsi créé (attributs du module).

Pour faire en sorte que le fichier soit d’abord recherché dans le répertoire ou se trouve la commande d’importation (import relatif), on utilisera from . import mod.

Effet de l’instruction « import module1 »

  • On accède aux attributs du module avec l’opérateur « . »
  • module1.py :
#!python
prompt = '%'
def printer(x):
  print(prompt,x)
  • ligne de commandes :
>>> import module1


>>> module1.printer('Hello')
% Hello


  • variante relative : from . import module1

Effet de l’instruction « import module1 as m1 »

  • module1.py :
#!python
prompt = '%'
def printer(x):
  print(prompt,x)
  • ligne de commandes :
>>> import module1 as m1


>>> m1.printer('Hello')
% Hello


  • variante relative : from . import module1 as m1

Effet de l’instruction « from module1 import printer »

  • module1.py :
#!python
prompt = '%'
def printer(x):
  print(prompt,x)
  • ligne de commandes :
>>> from module1 import printer


>>> printer('Hello')
% Hello


  • variante relative : from .module1 import printer
#!html
<br clear=right>

Effet de l’instruction « from module1 import printer as pr »

  • module1.py :
#!python
prompt = '%'
def printer(x):
  print(prompt,x)
  • ligne de commandes :
>>> from module1 import printer as pr


>>> pr('Hello')
% Hello


  • variante relative : from .module1 import printer as pr

Effet de l’instruction « from module1 import * »

  • module1.py :
#!python
prompt = '%'
def printer(x):
  print(prompt,x)
  • ligne de commandes :
>>> from module1 import *


>>> printer('Hello')
% Hello


  • variante relative : from .module1 import *

Import et from sont des instructions d’affectation

  • Elles peuvent être insérées n’importe où dans le code.
  • Elles sont interprétées à l’éxécution.
  • Les variables importées, si elles existaient auparavant, sont redirigées sur les nouvelles valeurs ou objets créés en mémoire (et on perd les anciennes valeurs).
  • Il n’y a aucun contrôle d’accès, pas d’attributs privés ou publics. Tout élément d’un module peut être lu et modifié, à condition de disposer d’une variable qui le désigne. sys.path peut-être redéfini à tout moment, affectant les futures instructions d’importation.

Restreindre « from * »

Deux moyens permettent de limiter les « dégâts » d’un from module import * :
1. préfixer une variable par « _ » pour que son nom ne soit pas importé.

#!python
x = 1
y = 2
_z = 3

1. lister explicitement les noms à importer à l’aide de la liste all

#!python
x = 1
y = 2
z = 3
all = [ 'x', 'y' ]

Quizz

  • module1.py :
#!python
prompt = '%'
def printer(x):
  print(prompt,x)

1. « % Hello » ou « > Hello » ?

>>> m = 2


>>> import module1 as m

>>> m.prompt = '>'

>>> m.printer('Hello')


#!comment
A la différence de C++, qui distingue
variables ordinaires, références, pointeurs.
passage d'arguments par valeur, référence, pointeur.

1. « # Hello » ou « % Hello » ou « > Hello » ?

>>> prompt = '#'


>>> from module1 import *

>>> prompt = '>'

>>> printer('Hello !')


Fonction prédéfinie reload()

Pour des raisons d’efficacité, lorsqu’on importe plusieurs fois le même module, seule la première instruction à de l’effet.
Par contre, si on a modifié le code d’un module, on veut pouvoir forcer sa ré-importation.
C’est le but de la fonction reload() :

  • N’est autorisé que pour un module déjà importé.
  • Ré-éxécute les instructions du fichier, et replace les nouveaux attributs au sein du module existant.
  • Le code client qui utilise le nom de module verra les nouveaux objets.
  • ATTENTION : le code client qui utilise des variables créées par from verra les anciens objets.
  • ATTENTION : reload() n’est pas transitif.
  • module1.py :
#!python
prompt = '%'
def printer(x):
  print(prompt,x)
  • ligne de commandes :
>>> import module1 as m1


>>> m1.prompt = '>'

>>> m2 = m1

>>> from module1 import *

>>> m1.prompt, m2.prompt, prompt
( '>' , '>' , '>' )

>>> reload(m1)

>>> m1.prompt, m2.prompt, prompt
( '%' , '%' , '>' )


#!comment
Mais reload n'est autorisé que sur un module déjà importé, travaille au sein de ce module, et surtout n'est pas transitif.
Morale, sur un gros programme plein de modules : renoncer au from, renoncer à l'interpréteur, renoncer à reload.

Résumé

  • Lors du premier appel à import ou from, le fichier correspondant est intégralement compilé, éxécuté et chargé en mémoire.
  • import crée une variable désignant l’ensemble du module.
  • from crée des variables vers les éléments du module.
  • reload() force la ré-éxécution des instructions du fichier.

Quelques pièges, truc et astuces

  • Attention à l’instruction from, qui peut écraser des variables déjà utilisées avec celles de même nom issues du module.
  • Attention à l’usage simultané de from et de reload.
  • Parce qu’un nom de fichier python est appelé à être utilisé comme module de même nom, il faut suivre les règles s’appliquant aux noms de variables. Evitez print.py !
#!comment
Morale : l'utilisation directe de l'interpréteur, pour débugguer, ne fonctionne que sur les petits programmes ; pour des gros programmes, avec modules, mieux vaut revenir à un modèle de développement ou pour tester on relance à chaque fois le programme from scratch ; reload est trop dangereux.
#!html
<br clear=right>

Quizz

3. Quels affichages ?

#!python
# fichier small.py
x = 1
y = [ 2, 3 ]
>>> from small import x, y


>>> x = 4

>>> y[0] = 5

>>> import small

>>> small.x, small.y
... ?

>>> reload(small)

>>> small.y, y
... ?


1. Quels affichages ?

#!python
# fichier module1.py
prompt = '%'
def printer(x):
  print(prompt,x)
#!python
# fichier module2.py
from module 1 import prompt
def printer(x):
  print(prompt,x)
>>> import module1 as m1


>>> import module2 as m2

>>> m1.prompt = '*'

>>> m2.printer('Hello')
... ?

>>> reload(m2)

>>> m2.printer('Hello')
... ?

>>> reload(m1)

>>> m2.printer('Hello')
... ?

>>> reload(m2)

>>> m2.printer('Hello')
... ?


#!comment
A la différence de C++, qui distingue
variables ordinaires, références, pointeurs.
passage d'arguments par valeur, référence, pointeur.

Bonnes pratiques

  • Fuyez from et reload.
  • Evitez les « globales » (noms définis au plus haut niveau d’un fichier, hors de toute fonction).

Exercice

Écrire un module regroupant des fonctions permettant de manipuler un fichier :

  • count(fichier) : renvoie le nombre de lignes, de mots et de lettres.
  • head(fichier,n) : renvoie les n premières lignes.
  • tail(fichier,n) : renvoie les n dernières lignes.
  • find(fichier,str) : affiche toutes les lignes contenant la chaine str.
  • replace(fichier,str1, str2) : affiche à l’écran toutes les lignes du fichier, en remplaçant chaque occurrence de str1 par str2.

Références

© David Chamont, Laboratoire Leprince-Ringuet (LLR) / IN2P3 / CNRS, Ecole polytechnique / Université Paris-Saclaybr
© Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative Commons Attribution – Partage dans les Mêmes Conditions 4.0 International [=#license]


Attachments