python modules

Identification

Infoforall

32 - Création de modules


Nous allons voir aujourd'hui ce qui se cache derrière le import.

Nous allons voir qu'on peut dissimuler à l'intérieur d'un module, l'encapsuler afin que l'utilisateur ne voit pas l'intégralité du code.

Que pourra alors en faire l'utilisateur ? L'utiliser comme une simple boîte à outils en n'utilisant que les fonctions ... d'interface et la documentation destinée à un utilisateur extérieur.

Prérequis : un peu de Turle

Logiciel nécessaire pour l'activité : Python 3 : Thonny, IDLE ...

Evaluation ✎ :

1 - Principe de la création d'un module

Imaginons que vous vouliez créer pour un enfant un ensemble de fonctions permettant de dessiner des choses. Pas facile de lui faire comprendre rapidement et facilement le principe de Tkinter ou de Turtle (basé sur Tkinter d'ailleurs...)

On décide donc de construire notre fichier Python de la façon habituelle :

  1. La documentation globale du fichier
  2. Les importations
  3. Les déclarations de classes
  4. Les déclarations des fonctions non utilisables par cet enfant (le code est compliqué)
  5. Les déclarations des fonctions basiques réalisées pour lui
  6. Le programme principal

01° Enregistrer ce programme dans Thonny en le nommant prog1.py et en le plaçant dans un dossier activite_module.

📁 activite_module

📄 prog1.py

Tester le programme. 130 lignes pour deux pauvres formes. Tranquille.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
'''Ce fichier permet de dessiner deux formes à l'aide des deux fonctions suivantes + triangle(cote, infos, coordonnees) + arc_de_cercle(rayon, angle, infos, coordonnees) Exemples d'utilisation : >>> divers1 = {'écriture':'blue', 'fond':'#FF88FF', 'épaisseur':5} >>> triangle(50, divers1, (50,100)) >>> arc_de_cercle(75, 360, divers1, (200,-200)) ''' # Importation import turtle as trt import random as rd # Pas de classes # Déclaration des fonctions qui ne sont pas dans l'interface-programmation def nouveau_stylo(ecriture, fond, largeur): '''Renvoie la référence d'un stylo configuré :: param ecriture(str) :: la couleur d'écriture ('red', '#FF0000') :: param fond(str) :: la couleur de fond pour ce stylo :: param largeur(int) :: la largeur du trait :: return (Turtle) :: renvoie un objet de la classe Turtle ''' feutre = trt.Turtle() feutre.color(ecriture) feutre.fillcolor(fond) feutre.pensize(largeur) feutre.speed(5) return feutre def deplacer(feutre, x, y): '''Lève le feutre, déplace le feutre et abaisse le feutre :: param feutre(Turtle) :: la référence du crayon :: param x(int) :: coordonnée horizontale (abscisse) :: param y(int) :: coordonnée verticale (ordonnée) :: return (None) :: c'est une fonction sans retour .. effet de bord :: modifie l'état de feutre ''' feutre.penup() # On lève la pointe feutre.setpos(x, y) # On déplace le crayon feutre.pendown() # On abaisse la pointe def trace_triangle(feutre, cote): '''Trace un triangle (equilatéral) à l'aide du crayon feutre :: param ftr(Turtle) :: la référence du crayon :: param cote(int) :: la valeur en pixel des côtés :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr ''' feutre.begin_fill() for x in range(3): feutre.forward(cote) feutre.left(120) feutre.end_fill() feutre.hideturtle() def trace_arc(feutre, rayon, angle): '''Trace un arc de cercle à l'aide du crayon feutre :: param ftr(Turtle) :: la référence du crayon :: param rayon(int) :: la valeur en pixel du rayon :: param angle(int) :: l'angle à tracer (360 pour un cercle) :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr ''' feutre.begin_fill() feutre.circle(rayon, angle) feutre.end_fill() feutre.hideturtle() # Déclarations des fonctions d'interface-programmation def triangle(cote, infos, coordonnees): '''Trace un triangle (equilatéral) à partir des infos et aux bonnees coordonnées :: param cote(int) :: la valeur en pixel des côtés :: param infos(dict) :: un dictionnaire {"écriture":str, "fond":str, "épaisseur":int} :: param coordonnees(tuple (int,int) ) :: un tuple (x,y) ''' ecriture = infos['écriture'] fond = infos['fond'] epaisseur = infos['épaisseur'] x = coordonnees[0] # ou x,y = coordonnees y = coordonnees[1] feutre = nouveau_stylo(ecriture, fond, epaisseur) deplacer(feutre, x, y) trace_triangle(feutre, cote) return feutre def arc_de_cercle(rayon, angle, infos, coordonnees): '''Trace un arc de triangle à partir des infos et aux bonnees coordonnées :: param rayon(int) :: la valeur en pixel du rayon :: param angle(int) :: la valeur en ° de l'angle :: param infos(dict) :: un dictionnaire {"écriture":str, "fond":str, "épaisseur":int} :: param coordonnees(tuple (int,int) ) :: un tuple (x,y) ''' ecriture = infos['écriture'] fond = infos['fond'] epaisseur = infos['épaisseur'] x = coordonnees[0] # ou x,y = coordonnees y = coordonnees[1] feutre = nouveau_stylo(ecriture, fond, epaisseur) deplacer(feutre, x, y) trace_arc(feutre, rayon, angle) return feutre # Programme principal divers1 = {'écriture':'blue', 'fond':'#FF88FF', 'épaisseur':5} triangle(50, divers1, (50,100)) arc_de_cercle(75, 360, divers1, (200,-200))
Une fonction pour une tâche

On se limite à créer des fonctions relativement simples à lire : elles ne doivent réaliser qu'une tâche limitée.

Comment réaliser des tâches complexes alors ? En créant des fonctions qui font appel aux fonctions basiques précédentes.

On rappelle aussi qu'on sépare clairement :

  1. Les fonctions qui gèrent juste les données sans se préoccuper de l'interface-graphique sur laquelle on va les utiliser pour créer un affichage.
  2. Les fonctions qui permettent l'interaction avec l'utilisateur via une interface-texte (Console ?) ou une interface-graphique (Tkinter, Turle, Qt...)

Si on analyse le code précédent :

Les fonctions "basiques"

  1. nouveau_stylo permet juste de créer un nouvel objet Turtle en utilisant le constructeur Turtle() et en modifiant son état à l'aide de différentes méthodes : color, fillcolor...
  2. deplacer permet juste de déplacer le stylo (en levant la pointe avant pour ne pas tracer quelque chose lors du déplacement).
  3. trace_triangle permet juste de tracer un triangle mais a besoin de recevoir une instance de Turtle pour tracer.
  4. trace_arc permet juste de tracer un arc de cercle mais a besoin de recevoir une instance de Turtle pour tracer.

Les fonctions "plus complexes"

  1. triangle trace un triangle en utilisant les fonctions précédentes. Elle renvoie l'instance utilisée pour tracer au besoin.
  2. arc_de_cercle trace un arc de cercle en utilisant les fonctions précédentes. Elle renvoie l'instance utilisée pour tracer au besoin.

02° Quel est le nom permettant de trouver les fonctions stockées dans le module turtle avec l'importation visible ci-dessous ?

13 14 15 16 17
# Importation import turtle as trt import random as rd

Comment trouver la fonction randint avec cette configuration ?

...CORRECTION...

Regardons maintenant comment marche basiquement l'importation : on aimerait bien cacher une partie des lignes pour que l'utilisateur ai l'impression que c'est facile à faire.

Nous allons couper le code et le placer dans deux fichiers :

📁 activite_module

📄 prog1.py

📄 prog2.py

📄 codecache.py

03° Enregistrer le programme suivant dans Thonny en le nommant codecache.py et en le plaçant dans un dossier activite_module.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
'''Ce fichier permet de dessiner deux formes à l'aide des deux fonctions suivantes + triangle(cote, infos, coordonnees) + arc_de_cercle(rayon, angle, infos, coordonnees) Exemples d'utilisation : >>> divers1 = {'écriture':'blue', 'fond':'#FF88FF', 'épaisseur':5} >>> triangle(50, divers1, (50,100)) >>> arc_de_cercle(75, 360, divers1, (200,-200)) ''' # Importation import turtle as trt import random as rd # Pas de classes # Déclaration des fonctions qui ne sont pas dans l'interface-programmation def nouveau_stylo(ecriture, fond, largeur): '''Renvoie la référence d'un stylo configuré :: param ecriture(str) :: la couleur d'écriture ('red', '#FF0000') :: param fond(str) :: la couleur de fond pour ce stylo :: param largeur(int) :: la largeur du trait :: return (Turtle) :: renvoie un objet de la classe Turtle ''' feutre = trt.Turtle() feutre.color(ecriture) feutre.fillcolor(fond) feutre.pensize(largeur) feutre.speed(5) return feutre def deplacer(feutre, x, y): '''Lève le feutre, déplace le feutre et abaisse le feutre :: param feutre(Turtle) :: la référence du crayon :: param x(int) :: coordonnée horizontale (abscisse) :: param y(int) :: coordonnée verticale (ordonnée) :: return (None) :: c'est une fonction sans retour .. effet de bord :: modifie l'état de feutre ''' feutre.penup() # On lève la pointe feutre.setpos(x, y) # On déplace le crayon feutre.pendown() # On abaisse la pointe def trace_triangle(feutre, cote): '''Trace un triangle (equilatéral) à l'aide du crayon feutre :: param ftr(Turtle) :: la référence du crayon :: param cote(int) :: la valeur en pixel des côtés :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr ''' feutre.begin_fill() for x in range(3): feutre.forward(cote) feutre.left(120) feutre.end_fill() feutre.hideturtle() def trace_arc(feutre, rayon, angle): '''Trace un arc de cercle à l'aide du crayon feutre :: param ftr(Turtle) :: la référence du crayon :: param rayon(int) :: la valeur en pixel du rayon :: param angle(int) :: l'angle à tracer (360 pour un cercle) :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr ''' feutre.begin_fill() feutre.circle(rayon, angle) feutre.end_fill() feutre.hideturtle() # Déclarations des fonctions d'interface-programmation def triangle(cote, infos, coordonnees): '''Trace un triangle (equilatéral) à partir des infos et aux bonnees coordonnées :: param cote(int) :: la valeur en pixel des côtés :: param infos(dict) :: un dictionnaire {"écriture":str, "fond":str, "épaisseur":int} :: param coordonnees(tuple (int,int) ) :: un tuple (x,y) ''' ecriture = infos['écriture'] fond = infos['fond'] epaisseur = infos['épaisseur'] x = coordonnees[0] # ou x,y = coordonnees y = coordonnees[1] feutre = nouveau_stylo(ecriture, fond, epaisseur) deplacer(feutre, x, y) trace_triangle(feutre, cote) return feutre def arc_de_cercle(rayon, angle, infos, coordonnees): '''Trace un arc de triangle à partir des infos et aux bonnees coordonnées :: param rayon(int) :: la valeur en pixel du rayon :: param angle(int) :: la valeur en ° de l'angle :: param infos(dict) :: un dictionnaire {"écriture":str, "fond":str, "épaisseur":int} :: param coordonnees(tuple (int,int) ) :: un tuple (x,y) ''' ecriture = infos['écriture'] fond = infos['fond'] epaisseur = infos['épaisseur'] x = coordonnees[0] # ou x,y = coordonnees y = coordonnees[1] feutre = nouveau_stylo(ecriture, fond, epaisseur) deplacer(feutre, x, y) trace_arc(feutre, rayon, angle) return feutre # Programme principal if __name__ == '__main__' : divers1 = {'écriture':'blue', 'fond':'#FF88FF', 'épaisseur':5} triangle(50, divers1, (50,100)) arc_de_cercle(75, 360, divers1, (200,-200))
if __name__ == '__main__' :

Cette instruction conditionnelle veut dire qu'on ne va appliquer les instructions des lignes 130, 131 et 132 que si on lance ce code directement, en lancant le fichier lui-même.

04° Lancer ce programme pour vérifier que le code fonctionne encore.

05° Enregistrer le court programme suivant dans Thonny en le nommant prog2.py et en le plaçant dans un dossier activite_module.

1 2 3
divers1 = {'écriture':'yellow', 'fond':'#88FF88', 'épaisseur':5} triangle(50, divers1, (50,100)) arc_de_cercle(75, 360, divers1, (200,-200))

📁 activite_module

📄 prog1.py

📄 prog2.py

📄 codecache.py

Vos deux fichiers devraient donc se situer dans le même répertoire.

Lancez ! Echec.

NameError: name 'triangle' is not defined

Ceci est normal : la fonction triangle ne se trouve en effet pas dans le même fichier.

Comme les deux fichiers sont au même endroit, nous allons pouvoir le trouver facilement : nous allons importer celui-ci et rajouter ce même alias devant les fonctions pour lui signaler d'aller chercher cette fonction dans ce fichier.

06° Enregistrer le court programme suivant dans Thonny en le nommant prog3.py et en le plaçant dans un dossier activite_module.

1 2 3 4 5
import codecache divers1 = {'écriture':'yellow', 'fond':'#88FF88', 'épaisseur':5} codecache.triangle(50, divers1, (50,100)) codecache.arc_de_cercle(75, 360, divers1, (200,-200))

📁 activite_module

📄 prog1.py

📄 prog2.py

📄 prog3.py

📄 codecache.py

Si vous lancez le fichier prog3.py vous devriez voir que cette fois cela fonctionne. De plus, on voit que les dessins sont verts et jaunes. Il s'agit donc bien des dessins provoqués par notre programme à l'aide des fonctions qui sont situés dans l'autre fichier.

07° Pourquoi les instructions contenues dans le fichier codecache.py et réalisant les dessins violets-mauves n'ont pas été exécutés lors de l'importation du fichier dans notre programme ?

...CORRECTION...

A cause du if __name__ == '__main__' :.

Cette fois, lorsqu'on exécute ces lignes de codes, le programme "principal" n'est pas lié à ce fichier : c'est le programme prog3.py qui a été lancé et qui est considéré comme le programme principal. C'est lui dont l'attribut spécial __name__ est évalué à '__main__'.

Lors de l'importation du fichier codecache.py, l'interpréteur Python le place en mémoire et arrive donc sur la ligne du test if __name__ == '__main__' :. Mais cette fois, ces lignes ne sont pas liées au programme principal mais sont simplement intégrées au programme. La condition est donc fausse et il ne tracera rien.

Nous sommes donc parvenu à "cacher" le code des fonctions. On peut encore faire plus discret mais il faudra s'en méfier : nous allons voir une importation permettant de simplement taper le nom des fonctions situées dans le fichier codecache.py. Pourquoi ne pas systématiquement le faire ? Tout simplement car si vous faites celma, Python n'aura plus de possibilité de distinguer par exemple deux fonctions trouver portant le même nom mais situés dans des fichiers différents. L'une des versions sera écrasée...

Voyons néanmoins comment on peut faire cela. Ici, pas de risque de voir l'une des fonctions se faire écrase.

08° Enregistrer le court programme suivant dans Thonny en le nommant prog4.py et en le plaçant dans un dossier activite_module.

1 2 3 4 5
from codecache import triangle, arc_de_cercle divers1 = {'écriture':'yellow', 'fond':'#88FF88', 'épaisseur':5} triangle(50, divers1, (50,100)) arc_de_cercle(75, 360, divers1, (200,-200))

📁 activite_module

📄 prog1.py

📄 prog2.py

📄 prog3.py

📄 prog4.py

📄 codecache.py

Si vous lancez le fichier prog4.py, vous devriez voir que cette fois cela fonctionne. De plus, on voit que les dessins sont verts et jaunes. Il s'agit donc bien des dessins provoqués par notre programme à l'aide des fonctions qui sont situés dans l'autre fichier.

from module import des_fonctions vs import module

La différence ?

Il faut savoir que import permet d'accèder directement à ce qu'on place derrière.

Avec import module, on importe le module. Il garde donc son espace des noms : il faut donc rajouter le nom du module suivi d'un point pour accéder au contenu. Pas de risque de collusion entre deux fonctions portant le même nom dans deux fichiers différents.

1 2 3 4 5
import codecache divers1 = {'écriture':'yellow', 'fond':'#88FF88', 'épaisseur':5} codecache.triangle(50, divers1, (50,100)) codecache.arc_de_cercle(75, 360, divers1, (200,-200))

Avec un from module import x, on importe directement x, une fonction par exemple. Elle intègre alors l'espace des noms du programme en cours et on fait en faire l'appel directement. Sans mettre le nom du module devant.

1 2
from codecache import triangle, arc_de_cercle triangle(50, divers1, (50,100))
Ne jamais écrire cela en NSI : from module import *

L'étoile est le caractère magique, celui qui remplace tous les autres.

En agissant ainsi, vous importer dans l'espace des noms de votre programme, tous les noms de variables, de fonctions et de classes qui sont dans le fichier visé. Du coup, les choses sont grandes d'écraser des choses.

Donc, évitez ce code là en NSI :

1
from math import *
Module

Un module est un fichier contenant des fonctions qu'on pourra importer comme on le veut depuis un autre programme.

Pour importer les fonctions, les constantes et autres documentations contenues dans le module, il faut utiliser import.

2 - Module dans un package

Notre code est bien caché, mais pas encore assez.

Le fichier Python est en effet directement accessible, dans le même répertoire.

Nous allons maintenant créer un package : il s'agit du nom donné à un répertoire qui va contenir les fichiers Python qu'on veut dissimuler.

Création d'un package en Python

Un package est donc un conteneur permettant de stocker des modules.

En Python, la création d'un package contenant un ou plusieurs modules est vraiment facile : il suffit

  1. de créer un répertoire en lui donnant un nom clair et explicite. Ce nom sera celui du package.
  2. d'y placer un fichier texte vide mais dont le nom est __init__.py (oui, comme la méthode initialisateur)
  3. de placer les fichiers Pyhon des modules que vous désirez rendre accessibles par ce package.

09° Créer maintenant la structure suivante en déplacant simplement le fichier codecache.py dont le répertoire voulu et en créant le fichier-texte vide __init__.py avec Thonny (il suffit de créer un nouveau fichier avec Thonny et de l'enregistrer immédiatement).

📁 activite_module

📄 prog1.py

📄 prog2.py

📄 prog3.py

📄 prog4.py

📁 activite

📄 codecache.py

📄 __init__.py

10° Enregistrer un fichier prog5.py avec Thonny. Vous y placerez tour à tour les versions suivantes :

1 2 3 4 5
from activite.codecache import triangle, arc_de_cercle divers1 = {'écriture':'yellow', 'fond':'#88FF88', 'épaisseur':5} triangle(50, divers1, (50,100)) arc_de_cercle(75, 360, divers1, (200,-200))

📁 activite_module

📄 prog1.py

📄 prog2.py

📄 prog3.py

📄 prog4.py

📄 prog5.py

📁 activite

📄 codecache.py

📄 __init__.py

1 2 3 4 5
from activite import codecache divers1 = {'écriture':'yellow', 'fond':'#88FF88', 'épaisseur':5} codecache.triangle(50, divers1, (50,100)) codecache.arc_de_cercle(75, 360, divers1, (200,-200))
1 2 3 4 5
from activite import codecache as cc divers1 = {'écriture':'yellow', 'fond':'#88FF88', 'épaisseur':5} cc.triangle(50, divers1, (50,100)) cc.arc_de_cercle(75, 360, divers1, (200,-200))
1 2 3 4 5
import activite.codecache as cc divers1 = {'écriture':'yellow', 'fond':'#88FF88', 'épaisseur':5} cc.triangle(50, divers1, (50,100)) cc.arc_de_cercle(75, 360, divers1, (200,-200))

3 - Documentation des modules

Reprenons la structure d'un programme tel qu'on les réalise depuis le début :

  1. La documentation globale du fichier
  2. Les importations
  3. Les déclarations de CONSTANTES et autres variables globales éventuelles
  4. Les déclarations de Classes
  5. Les déclarations des fonctions et on distingue :
    1. les fonctions gestion de données ne gérant que les données
    2. les fonctions liées à l'interface-utilisateur (interface texte si console ou interface graphique si Tkinter, Turle ou Qt)
  6. Le programme principal,

    • avec en option un bloc lié à une instruction conditionnelle if __name__ == '__main__' :. C'est dans cette structure qu'on plaçait les tests automatisées (via le module doctest ou en utilisant juste des assertions).

Il y a deux différences dans la structure d'un module :

  1. Le bloc if __name__ == '__main__' : est maintenant obligatoire sinon les instructions vont s'éxécuter dès qu'on importera le module
  2. Toutes les fonctions ne seront pas censées être disponibles de l'extérieur : nous allons distinguer deux nouveaux types de fonctions :
    • Les fonctions d'interface-programmation : ces fonctions sont officiellemenet destinées à être appellées de l'extérieur du module
    • Les fonctions hors interface-programmation : ces fonctions réalisent de tâches pour permettre aux fonctions précédentes de fonctionner. Mais elles ne sont pas censées être appellées en accès direct.

La structure d'un module :

  1. La documentation du module
  2. Les importations
  3. Les déclarations de CONSTANTES et autres variables globales éventuelles
  4. Les déclarations de Classes
  5. Les déclarations des fonctions internes n'appartenant pas à l'interface-programmation et on distingue :
    1. les fonctions gestion de données ne gérant que les données
    2. les fonctions liées à l'interface-utilisateur (interface texte si console ou interface graphique si Tkinter, Turle ou Qt)
  6. Les déclarations des fonctions d'interface-programmation auxquelles on peut faire appel directement de l'extérieur. On distingue :
    1. les fonctions gestion de données ne gérant que les données
    2. les fonctions liées à l'interface-utilisateur (interface texte si console ou interface graphique si Tkinter, Turle ou Qt)
  7. Le programme principal,

    • avec OBLIGATOIREMENT un bloc lié à une instruction conditionnelle if __name__ == '__main__' :. C'est dans cette structure qu'on placera les programmes de tests et de vérifications : soit via doctest, soit via des assertions.

Lors de la conception du module, on peut donc placer tous les tests de fonctionnement dans le bloc du programme principal : lorsqu'on lance directement le fichier du module, les tests sont faits. Par contre, lorsqu'utilisateur va l'utiliser, les tests ne seront pas fait.

Fonction native help

Cette fonction permet à l'utilisateur d'obtenir de l'aide via la documentation du module justement.

Elle ramène dans la console toute la documentation placée en début de module qui doit expliquer le fonctionnement globale et lister par exemple les fonctions d'interface disponibles.

Elle ramène également toute la documentation sur les fonctions internes au module.

>>> import turtle >>> help(turtle) Help on module turtle: NAME turtle MODULE REFERENCE https://docs.python.org/3.7/library/turtle The following documentation is automatically generated from the Python ...

On peut également chercher uniquement de la documentation sur l'une des fonctions :

>>> import turtle >>> help(turtle.pendown) Help on function pendown in module turtle: pendown() Pull the pen down -- drawing when moving. Aliases: pendown | pd | down No argument. Example: >>> pendown() >>> help(turtle.left) Help on function left in module turtle: left(angle) Turn turtle left by angle units. Aliases: left | lt Argument: angle -- a number (integer or float) Turn turtle left by angle units. (Units are by default degrees, but can be set via the degrees() and radians() functions.) Angle orientation depends on mode. (See this.) Example: >>> heading() 22.0 >>> left(45) >>> heading() 67.0

11° Utiliser la fonction native help de façon à visualiser dans la console la documentation du module.

Il faudra penser à vérifier au préalable que le module soit bien importé en mémoire et connaître son alias !

>>> import activite >>> help(activite) >>> help(activite.codecache)
>>> from activite import codecache >>> help(codecache)
>>> from activite import codecache as cc >>> help(cc)

...CORRECTION...

>>> import activite >>> help(activite) Help on package activite: NAME activite PACKAGE CONTENTS codecache FILE /home/rv/Documents/exomodule/activite/__init__.py >>> help(activite.codecache) Help on module activite.codecache in activite: NAME activite.codecache - Ce fichier permet de dessiner deux formes à l'aide des deux fonctions suivantes DESCRIPTION + triangle(cote, infos, coordonnees) + arc_de_cercle(rayon, angle, infos, coordonnees) Exemples d'utilisation : >>> divers1 = {'écriture':'blue', 'fond':'#FF88FF', 'épaisseur':5} >>> triangle(50, divers1, (50,100)) >>> arc_de_cercle(75, 360, divers1, (200,-200)) FUNCTIONS arc_de_cercle(rayon, angle, infos, coordonnees) Trace un arc de triangle à partir des infos et aux bonnees coordonnées :: param rayon(int) :: la valeur en pixel du rayon :: param angle(int) :: la valeur en ° de l'angle :: param infos(dict) :: un dictionnaire {"écriture":str, "fond":str, "épaisseur":int} :: param coordonnees(tuple (int,int) ) :: un tuple (x,y) deplacer(feutre, x, y) Lève le feutre, déplace le feutre et abaisse le feutre :: param feutre(Turtle) :: la référence du crayon :: param x(int) :: coordonnée horizontale (abscisse) :: param y(int) :: coordonnée verticale (ordonnée) :: return (None) :: c'est une fonction sans retour .. effet de bord :: modifie l'état de feutre nouveau_stylo(ecriture, fond, largeur) Renvoie la référence d'un stylo configuré :: param ecriture(str) :: la couleur d'écriture ('red', '#FF0000') :: param fond(str) :: la couleur de fond pour ce stylo :: param largeur(int) :: la largeur du trait :: return (Turtle) :: renvoie un objet de la classe Turtle trace_arc(feutre, rayon, angle) Trace un arc de cercle à l'aide du crayon feutre :: param ftr(Turtle) :: la référence du crayon :: param rayon(int) :: la valeur en pixel du rayon :: param angle(int) :: l'angle à tracer (360 pour un cercle) :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr trace_triangle(feutre, cote) Trace un triangle (equilatéral) à l'aide du crayon feutre :: param ftr(Turtle) :: la référence du crayon :: param cote(int) :: la valeur en pixel des côtés :: return (None) :: fonction sans retour .. effet de bord :: modifie l'état de ftr triangle(cote, infos, coordonnees) Trace un triangle (equilatéral) à partir des infos et aux bonnees coordonnées :: param cote(int) :: la valeur en pixel des côtés :: param infos(dict) :: un dictionnaire {"écriture":str, "fond":str, "épaisseur":int} :: param coordonnees(tuple (int,int) ) :: un tuple (x,y) FILE /home/rv/Documents/exomodule/activite/codecache.py
help()

4 - Bibliothèque

Bibliothèque

Un bibliothèque est le nom qu'on donne à un ensemble de modules, que ceux-ci sont encapsulés dans un package, ou pas.

Si on doit résumer :

  • Un module c'est un fichier python (analogie : un livre)
  • Une bibliothèque, c'est l'ensemble de plusieurs fichiers Python (analogie : plusieurs livres)
  • Un package, c'est le répertoire qui contient les livres (analogie : le meuble, l'armoire qui contient les livres)

En réalité, c'est même plus compliqué car un package peut contenir des modules ou d'autres packages.

Le mot bibliothèque est utilisable dans deux sens :

  1. Ensemble de plusieurs modules
  2. Un package qui contient plusieurs modules ou même d'autres packages

12° Dans la structure fournie, dire pour chaque élément si c'est un package, un module ou une bibliothèque.

📁 images

📄 __init__.py

📄 couleurs.py

📄 pixels.py

📁 rgb

📄 __init__.py

📄 gestionrgb.py

📁 encodage

📄 __init__.py

📄 technique1.py

📄 technique2.py

...CORRECTION...

📁 images : package et bibliothèque car contient plusieurs modules

📄 couleurs.py : module

📄 pixels.py : module

📁 rgb : package (ne contient qu'un module)

📄 gestionrgb.py : module

📁 encodage : package et une bibliothèque car contient deux modules

📄 technique1.py : module

📄 technique2.py : module

13° Comment utiliser une fonction encoder() du fichier tehnique1.py si on importe comme cela ?

>>> from images import encodage.technique1 as t1

...CORRECTION...

>>> from images import encodage.technique1 as t1 >>> t1.encoder()

14° Comment utiliser une fonction encoder() du fichier tehnique1.py si on importe comme cela ?

>>> import images as img

...CORRECTION...

>>> import images as img >>> img.encodage.technique1.encoder()

C'est un peu lourd non ?

Mais au moins, par de problème de collusion de nom...

5 - Projet

Vous allez créer maintenant plusieurs modules de dessin permettant de réaliser une rue comportant 4 immeubles.

Chaque immeuble aura les caractéristiques suivantes :

  • Les immeubles ont tous une largeur de 140 pixels,
  • Entre 1 à 5 étages (chaque étage faisant 80 pixels)
  • Chaque fenêtre fait 30 px sur 30 px
  • Une porte unique au rez-de-chaussée de 30 px sur 50px
  • Deux variations du toit
  • Il est possible que certaines fenêtres soient des portes-fenètres de 30 px sur 50 px

Ce projet est une idée originale du site Ostralo.net.

L'idée générale est :

  1. De rajouter un maximum de nouvelles fonctions de dessin dans le module codecache.py. Pour l'instant, on ne crée que des triangles et des arc de cercles. Les fonctions d'interface permettent de créer des dessins sans toucher une seule ligne de Turtle. De quelles formes de base aura-t-on besoin pour tracer l'immeuble ?
  2. De placer le module dans un package dessin
  3. De créer un programme rue.py qui va tracer 4 immeubles aux caractéristiques aléatoires à partir d'une fonction dessiner_immeuble qui ,elle, utilise les fonctions du module codecache.

📁 activite_module

📄 rue.py

📁 dessin

📄 __init__.py

📄 codecache.py

On devrait donc parvenir à créer un immeuble, puis 4 immeubles sans toucher directement une ligne de turtle mais en utilisant simplement les fonctions d'interface de dessin du module codecache.

Un exemple de contenu de rue.py" qui ne crée pas d'immeuble mais qui permet de comprendre le principe. Attention, il est volontairement non documenté. Il faudra justement rajouter la documentaion.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
# Importation from dessin.codecache import triangle # Fonction gestion des données def determiner_immeuble(numero): reponse = {} reponse['couleur_facade'] = 'red' reponse['numero'] = numero return reponse # Fonctions d'interface graphique def dessiner_facade(informations): facade = {} facade['écriture'] = 'blue' facade['fond'] = informations['couleur_facade'] facade['épaisseur'] = 5 x = informations['numero']*10 y = informations['numero']*50 triangle(50, facade, (x,y)) def dessiner_immeuble(informations:dict): dessiner_facade(informations) # Programme principal for x in range(4): informations = determiner_immeuble(x) dessiner_immeuble(informations)

Mettons nous d'accord sur un prototype commun de la fonction dessiner_immeuble :

1
def dessiner_immeuble(informations:dict):

L'intérêt du dictionnaire est évidente ici : on pourra envoyer autant de caractéristiques qu'on veut. Au début il n'y aura que le numéro dans la rue, le nombre d'étages et la couleur de la facade mais ensuite...

Comment avancer ?

Déjà en réalisant les prototypes des fonctions de base qui seront utilisées dans la fonction dessiner_immeuble.

  • une fonction pour déterminer aléatoirement le nombre d'étages ? (données ou interface, ça ?)
  • une fonction pour déterminer aléatoirement la couleur de la facade ? (données ou interface, ça ?)
  • une fonction pour tracer la façade en fonction du nombre d'étages, de la couleur de la facade et de sa position dans la rue ? (données ou interface ça ?)

Une fois le prototype réalisé, il suffira de rajouter les options à travers de nouvelles fonctions :

  • une fonction pour tracer une porte ?
  • une fonction pour tracer une fenêtre ?
  • une fonction pour tracer les fenêtres ?
  • une fonction pour tracer le toit ?
  • une fonction pour tracer une antenne ou un pigeon ?
  • ...

Vous êtes plusieurs. Au début, il faudrait donc :

  • Une personne qui se charge de créer les nouvelles fonctionnalités du module codecache : pour l'instant, on ne sait même pas tracer un rectangle...
  • Une personne qui tente de créer un premier prototype de rue.py permettant juste de tracer par exemple 4 trianges et arc_de_cercles ayant les bonnes caractéristiques et les bonnes positions.
  • Une personne qui tente de créer les prototypes des fonctions qui devront être codées dans rue.py en commençant par le fondamental.

Ensuite, il faudra vous répartir les fonctions à coder.

6 - FAQ

Et comment installer un package pour qu'il soit reconnaissable de partout, comme math par exemple ?

Pour cela, il faut que le package soit installé dans un répertoire dans lequel Python cherche lorsqu'on lui demande d'importer des choses.

Pour connaitre ces répertoires, on peut utiliser le module sys qui permet d'interagir avec le système d'exploitation.

>>> import sys >>> sys.path [ '/home/rv/Documents/exomodule', '/home/rv/apps/thonny/lib/python37.zip', '/home/rv/apps/thonny/lib/python3.7', '/home/rv/apps/thonny/lib/python3.7/lib-dynload', '/home/rv/.local/lib/python3.7/site-packages', '/home/rv/apps/thonny/lib/python3.7/site-packages' ]

On voit que notre interpréteur Python lié à Thonny va chercher :

  • d'abord dans le répertoire en cours (c'est pour cela qu'il peut trouver un module placé au même endroit que le fichier du programme !)
  • Puis dans différents répertoires de Thonny lui-même
  • Puis dans un répertoire de la version Python utilisée par Thonny
  • Puis dans un dernier répertoire de Thonny s'il n'a pas trouvé son bonheur avant

Si vous placez votre module dans un de ces répertoires, vous allez pouvoir importer directement vos modules ou vos packages.

Et on peut le faire ?

Oui mais c'est dangereux.

Lorsque l'interpréteur cherche un module, il s'arrête sur le premier fichier portant le bon nom qu'il trouve.

Si vous mettez vos propres modules dans les répertoires de Python, vous risquez potentiellement de donner à votre package ou votre module, le nom d'un module préexistant. Et... l'un des deux ne sera plus trouvable.

C'est ce qui se passe lorsque vous réalisez un programme qui fait un petit truc au hasard et que vous le nommer test.py ou random.py : vous placez un module dans le répertoire courant qui porte le même nom que le module random ou le module test. Du coup, aucun autre programme de ce répertoire ne pourra accéder au vrai module random tant que votre petit fichier python portera le même nom.

Alors faire ça dans le répertoire global des modules, ce n'est pas très malin...

Nous irons un peu plus loin dans une autre activité. Vous y verrez notamment qu'on peut montrer la correction d'un algorithme récursif.

Activité publiée le 24 11 2020
Dernière modification : 03 12 2020
Auteur : ows. h.