Connexion élèves

Choisir le(s) module(s) à installer :

Modularité

La modularité d'un programme informatique traduit le fait que celui-ci découpe les actions qu'il doit faire en taches les plus élémentaires possibles; chacune de ces taches correspond généralement à une fonction.

Un programme très modulaire a un code qui est beaucoup plus clair à lire, et sa maintenance est beaucoup plus facile.

Un module est un fichier contenant du code ( dans notre cas, en Python, c’est-à-dire tout fichier avec l’extension .py), que l'on pourra ensuite importer dans son propre script de façon à utiliser les fonctionnalités que ce code additionnel propose.

Les modules permettent alors la séparation et donc une meilleure organisation du code. En effet, il et courant dans un projet de découper son code en différents fichiers qui vont contenir des parties cohérentes du programme final pour faciliter la compréhension générale du code, la maintenance et le travail d’équipe si on travaille à plusieurs sur le projet.

En Python, on peut distinguer trois grandes catégories de module en les classant selon leur éditeur :

  • Les modules de la distribution standard, qui sont disponibles directement et peuvent donc être automatiquement importés par Python.
    Nous avons déjà utilisé de nombreux modules de ce type : random, math, turtle etc...
  • Les modules développés par des développeurs externes, qui doivent dans ce cas être préalablement installés sur la machine à partir d'un dépôt distant, comme PyPI, à l'aide du programme pip.
    Le traceur de graphique Matplotlib, ou le module de création de jeu Pyxel font partie de cette catégorie.
  • Les modules que l'on développe soi-même.
legos

Importer un module dans un script Python

Quelle que soit l'origine d'un module, la manière de l'importer dans son propre script est la même.

Mais il existe plusieurs façons d' importer une fonction ou un module dans un programme; essayons de comprendre les différentes possibilités :

La première méthode consiste à n'importer qu'une seule fonction d'un module, en la nommant :


from random import randint
print(randint(0,10))
		

Dans ce cas on ne peut utiliser que la fonction importée.
Mais il est bien entendu possible d'importer plusieurs fonctions du module, en séparant leurs noms par des virgules :


from random import randint, shuffle
l = [randint(1,100) for i in range(100)]
l.shuffle()
		

Si un module est largement utilisé, il devient alors compliqué d'importer de cette façon de nombreuses fonctions; on peut être tenté de taper :


from random import *
print(randint(0,10))
		

Très pratique mais un peu dangereux ... En effet, si vous utilisez deux modules de cette façon il peut y avoir une collision de nom : si deux modules comportent deux fonctions ayant le même nom, cela va poser problème...
Donc si l'on veut absolument importer des modules entiers dont les fonctions seront largement utilisées, il est préférable de taper :


import random
print(random.randint(0,10))
		

Dans ce cas, il est alors nécessaire de préfixer chaque fonction du module par le nom du module. Cela évite les confusions en cas d'homonymie.

Comme le nom du module peut être un peu long à taper et risque de revenir souvent (et également pour plus de lisibilité...), il est possible d'utiliser un alias pour le nom du module :


import random as rdm
print(rdm.randint(0,10))
		

Dans ce cas le préfixage est plus simple.

Lorsque l'on importe un module il est important de savoir ce que l'on fait. Il faut :

  • Dans la mesure du possible, limiter l'import aux fonctions que l'on va utiliser
  • Éviter d'utiliser from module import *
  • Utiliser des alias judicieux pour un code plus lisible

Aide sur un module

Lister les fonctions d'un module

Pour connaître la liste des fonctions proposées par un module, utiliser dans la console ( après avoir importé le module ! ) la commande : dir(module) :


>>> dir(random)		

['BPF', 'LOG4', 'NV_MAGICCONST', 'RECIP_BPF', 'Random', 'SG_MAGICCONST', 'SystemRandom', 'TWOPI', '_ONE', '_Sequence', '_Set', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_accumulate', '_acos', '_bisect', '_ceil', '_cos', '_e', '_exp', '_floor', '_index', '_inst', '_isfinite', '_log', '_os', '_pi', '_random', '_repeat', '_sha512', '_sin', '_sqrt', '_test', '_test_generator', '_urandom', '_warn', 'betavariate', 'choice', 'choices', 'expovariate', 'gammavariate', 'gauss', 'getrandbits', 'getstate', 'lognormvariate', 'normalvariate', 'paretovariate', 'randbytes', 'randint', 'random', 'randrange', 'sample', 'seed', 'setstate', 'shuffle', 'triangular', 'uniform', 'vonmisesvariate', 'weibullvariate']	
		

Importance des annotations et de la docstring dans un module

Lorsque vous cherchez des informations sur un module ou une commande Python vous allez souvent sur internet...
Mais de nombreuses informations sont données directement dans la console Python grâce à la commande help()

Documentation d'une classe/d'une fonction

Si vous tapez dans un shell Python :


>>> t=[]
>>> help(t)
		

Vous obtiendrez l'aide de base sur la classe list de Python, toutes les méthodes existantes, etc ....

Documentation d'un module

De la même façon on peut obtenir la documentation d'un module complet; dans un shell Python, tapez :


>>> help()
help> random
		

Vous obtenez ainsi l'aide complète du module ( tapez quit pour sortir de l'utilitaire d'aide )....
Cette aide est très dense et assez peu facile à parcourir, mais cela peut permettre de vérifier l'existence d'une fonction par exemple.

On peut également, après avoir importé le module, taper directement :


>>> help(random)
		

Documentation d'une fonction d'un module

Cette dernière fonctionnalité est la plus utilisée. Si vous avez un doute sur la syntaxe d'un fonction, plutôt que de traîner sur internet, tapez :


>>> help(random.randint)
		

...et vous obtenez immédiatement une aide succincte mais efficace :


Help on method randint in module random:

randint(a, b) method of random.Random instance
    Return random integer in range [a, b], including both end points.
		

Pour obtenir de l'aide sur une classe, un module, une fonction, il faut dans un premier temps utiliser la commande help()/

Mais d'où vient cette aide ? Les développeurs de python doivent donc documenter tout ce qu'ils font ?

En fait l'affichage généré par la commande help() est l'affichage de la docstring de chaque fonction !!!
C'est pourquoi il est essentiel de documenter correctement la docstring lors de la construction d'un module. C'est la première aide pour l'utilisateur de la fonction.
Cette aide est de qualité car elle vient du développeur ou du groupe de développeur directement.

La commande help() affiche la docstring d'une fonction, d'où son importance !

Pour vous entraîner à utiliser la fonction help() répondez aux questions ci-dessous :
  1. Combien de décimales de pi sont retenues dans le module math ?
  2. Que fait la fonction remainder ?
  3. Que renvoie la fonction factorielle (factorial) pour un nombre négatif ou un nombre non entier ?
  4. De quel type sont les deux arguments de la fonction gcd
  5. Quelle fonction du module pourrait calculer facilement la hauteur d'un arbre binaire complet équilibré de taille n ?

Valider les préconditions : les assertions

Quand on construit un module il est important de le documenter clairement afin que d'autres développeurs puissent l'utiliser.
Mais il est également important que ce module soit robuste.
Comme un développeur ne lit "pas toujours" (hum hum) l'intégralité de la docstring des fonctions qu'il utilise, il est nécessaire de protéger votre code des entrées incongrues (calcul de la factorielle d'une str, d'un nombre négatif...). Il existe pour cela les assertions. Lisez ce paragraphe qui vous expliquera en détail l'utilisation de la fonction assert.

Tester un module sans perturber le programme principal

Comme pour tous les codes que l'on écrit en python, il est nécessaire de tester notre production. Cependant, quand on code un module qui aura vocation à être importé et utilisé par d'autres programmeurs dans d'autres codes, la situation est un peu délicate ...
Il est possible de laisser des tests visibles dans un module, sans qu'ils ne s'exécutent quand on l'importe.
Pour cela il est nécessaire des placer ces tests sous l'instruction :

if __name__ == '__main__':
			      
En fait la variable __name__ est une variable réservée dans python et gérée par le langage lui même. Elle contient la chaîne de caractères '__main__' si on l'affiche dans le programme qui est directement exécuté, et la chaîne de caractères 'mon_module' si elle est affichée dans un module importé.

Une application : modultab

Structure

Maintenant que vous en savez un peu plus sur la modularité, nous allons créer un module permettant de gérer quelques fonctions avancées sur les tableau en python.
L'idée est la suivante : mettre au point un module modultab qui permet de réaliser des fonctions avancées sur des tableaux d'entiers (recherche de minimum et de maximum, somme et produit de tableau, calcul de dénivelée etc... )
Mais vous ne partez pas de rien, voici un programme principal qui utilise votre futur module modultab :

from modultab import *

t=[1,-3,52,14,-18,16,79,79,11,0]

print(minimum(t)) #affiche -18
print(maximum(t)) #affiche 79
print(somme(t)) #affiche 231
print(produit(t)) #affiche 0
print(longueur(t)) #affiche 10
print(moyenne(t)) #affiche 231/10
print(tri(t)) #affiche [-18, -3, 0, 1, 11, 14, 16, 52, 79, 79]
print(inverse(t)) #affiche [0,11,79,79,16,-18,14,52,-3,1]
print(deniv(t)) # affiche (152, -153)
      

Consignes

Vous devez donc créer le module python qui est importé en début de code (modultab.py). Il devra contenir :
  • Les fonctions listées dans le programme principal.
  • Les docstrings claires et détaillées de ces fonctions (c'est ce qui appairait quand on appelle l'aide du module avec help()).
  • Les tests de votre module sous la section if __name__ == '__main__' (voir ci-dessus).
  • le code de votre module devra être le plus...modulaire possible !😉
Il va de soit que vous ne pouvez pas utiliser les fonctions déjà existantes dans python comme : sort(), len(), min(), max() ETC ...
#corps du module if __name__ == '__main__' #tests du module

SOLUTION