Vous êtes sur la page 1sur 87

Les sous-programmes

Les sous-programmes

Notion de sous-programme

La librairie standard

Les fonctions originales

Les procédures

La description d’une fonction


Notion de sous-programme

Prenons l’exemple du programme suivant :

instruction1
instruction2
instruction3
instruction4 Blocs d’instructions identiques.
instruction5
instruction6
instruction2 Pour éviter d’avoir à écrire plusieurs fois les
instruction3 mêmes instructions, nous pouvons les écrire
instruction4 dans un sous-programme et faire appel à ce
instruction7 sous-programme chaque fois que le bloc doit
instruction8 être exécuté.
instruction2
instruction3
instruction4
instruction9
Notion de sous-programme

Le programme précédent peut aussi s’écrire de la façon suivante :

Programme principal
instruction1
SP Sous-programme (ou routine)
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
Appel du sous-programme
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Exécution du programme :

instruction1
SP
instruction5
instruction6 SP
SP instruction2
instruction7 instruction3
instruction8 instruction4
SP retour
instruction9
Notion de sous-programme

Un sous-programme est une suite d’instructions nommée et autonome.

Chaque fois qu’un sous-programme est appelé par son nom, ses instructions
sont exécutées comme si elles figurées dans le corps principal appelant.

Réduction de la redondance dans un programme.

La tâche d’un sous-programme doit être parfaitement définie.

Les sous-programmes permettent de décomposer le problème à traiter :


 Programme plus facile à lire,
 Erreurs plus faciles à détecter,
 Modifications plus aisées.
Notion de sous-programme

Un sous-programme est une partie indépendante du programme principal ou


des autres sous-programmes, testée et validée indépendamment.

L’indépendance d’un sous-programme permet en plus de l’intégrer dans un


module (librairie ou bibliothèque) utilisable par plusieurs programmes.

Librairie my_lib Programme 1 Programme 2

SP1 import my_lib import my_lib

SP2 instruction1 instruction1


instruction2 SP1
SP3 instruction3 instruction2
SP3 instruction3
instruction4 instruction4
instruction5 SP2
SP1
instruction6
Notion de sous-programme

Deux types de sous-programme se rencontrent habituellement dans les


langages de programmation :

 Les fonctions

 Les procédures

Les fonctions sont des routines qui retournent systématiquement une valeur au
programme appelant, ce qui permet de les utiliser dans des expressions.

Exemple :

>>> a = 2
>>> b = 5
>>> hypothenuse = sqrt(a**2 + b**2)
>>> hypothenuse
5.385164807134504
Notion de sous-programme

Les procédures sont des routines qui ne renvoient pas de valeur.

Une procédure génère un effet de bord qui peut être la modification d’une
valeur en mémoire, l’affichage d’un message à l’écran… (modification de
l’environnement d’exécution).

Exemple :

Soit une procédure, nommée double, dont le rôle est de multiplier par deux la
valeur de la variable globale n (modification de la valeur en mémoire).

>>> n = 5
>>> double()
>>> print('La valeur de n vaut maintenant', n)
La valeur de n vaut maintenant 10

L’instruction print est aussi une procédure car elle ne renvoie pas de valeur.
Elle modifie simplement l’environnement en affichant des valeurs… (il n’y a
pas d’apostrophes autour de la chaîne affichée).
Les sous-programmes

Notion de sous-programme

La librairie standard

Les fonctions originales

Les procédures

La description d’une fonction


La librairie standard : Les fonctions intégrées

La librairie standard regroupe les fonctions prédéfinies (ou pré-programmées)


qui sont des routines fréquemment utilisées fournies d’origine avec le langage
Python.

Fonctions prédéfinies intégrées au langage Python

Certaines routines standard considérées comme suffisamment génériques


pour intéresser la majorité des utilisateurs sont intégrées au langage Python et
sont ainsi directement chargées dans la mémoire de l’ordinateur au lancement
de l’interprète.

Leur utilisation ne nécessite aucune instruction préalable particulière.


La librairie standard : Les fonctions intégrées

Quelques exemples de fonctions prédéfinies intégrées au langage Python :

• print qui permet d’afficher des valeurs à l’écran.

• input qui permet de saisir des caractères au clavier.

• min et max qui permettent de déterminer respectivement les valeurs


minimale et maximale parmi toutes celles passées en paramètres.

• len qui permet de connaître la longueur d’une chaîne de caractères.


La librairie standard : Les Modules

Modules de la librairie standard

D’autres routines standard, plus spécifiques, et donc moins fréquemment


utilisées, sont également fournies mais ne sont pas chargées automatiquement
en mémoire au lancement de l’interprète Python.

Ces fonctions sont regroupées dans des fichiers séparés que l'on appelle des
modules.

Généralement, on essaie de regrouper dans un même module des ensembles


de fonctions apparentées que l'on appelle des bibliothèques ou librairies.
La librairie standard : Les Modules

Quelques exemples de librairies disponibles en standard :

• math : fonctions mathématiques

• statistics : fonctions mathématiques pour les statistiques

• string : fonctions de manipulation de chaînes de caractères

• zipfile : fonctions pour la gestion de fichiers archives

• http : fonctions pour la gestion des clients et serveurs HTTP

• wave : fonctions pour l’écriture et la lecture de fichiers audio au format WAV

• tkinter : fonctions pour la réalisation d’interfaces graphiques


La librairie standard : Les Modules

Quelques exemples de fonctions disponibles dans la librairie math :

• ceil(x) : retourne le plus petit entier supérieur ou égal à x

• factorial(x) : retourne la factorielle de x

• exp(x) : retourne e**x

• log10(x) : retourne le logarithme en base 10 de x

• sqrt(x) : retourne la racine carrée de x

• cos(x) : retourne le cosinus de x en radians

• sin(x) : retourne le sinus de x en radians

• tanh(x) : retourne la tangente hyperbolique de x

• pi : La constante mathématique  = 3.141592…


La librairie standard : Les Modules

La liste complète des librairies standard et des fonctions intégrées est


disponible sur :

https://docs.python.org/3/library/index.html
La librairie standard : Utilisation des fonctions standard

Pour pouvoir utiliser des fonctions standard dans vos programmes il faudra
d’abord les importer des différents modules.

Toute tentative d’utilisation d’une fonction standard sans importation


préalable se traduira par une erreur d’exécution.

>>> sqrt(2)
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
NameError: name 'sqrt' is not defined
La librairie standard : Utilisation des fonctions standard

Par exemple, si un programme n’utilise que la fonction standard sqrt qui se


trouve dans le module math, l’importation prendra la forme :

>>> from math import sqrt

La fonction sqrt est maintenant chargée en mémoire et peut être appelée


à partir de l’interprète :

>>> sqrt(2)
1.4142135623730951

Par convention, les instructions d'import de modules ou fonctions sont


écrites en début de programme.
La librairie standard : Utilisation des fonctions standard

Si plusieurs fonctions ou constantes prédéfinies d’un même module sont


utilisées dans le programme, elles peuvent être importées dans une seule
et même instruction.

Par exemple, si un programme utilise la fonction standard cos et la constante


prédéfinie pi qui se trouvent dans le module math, l’importation prendra la
forme :

>>> from math import cos, pi


>>> cos (3 * pi / 4)
-0.7071067811865475
La librairie standard : Utilisation des fonctions standard

Si plusieurs fonctions ou constantes prédéfinies issues de différents


modules sont utilisées dans le programme, elles doivent être importées les
unes après les autres.

Par exemple, si un programme utilise :

• la fonction standard cos et la constante prédéfinie pi qui se trouvent dans


le module math,

• et la fonction standard uniform du module random,

deux instructions d’importation sont nécessaires :

>>> from math import cos, pi


>>> from random import uniform

>>> cos(uniform(0, pi))


-0.2959191439918261
La librairie standard : Utilisation des fonctions standard

Une autre solution consiste à importer la totalité des fonctions qui se


trouvent dans un module.

Cette solution, qui consomme davantage de mémoire, doit être retenue


uniquement si la quasi-totalité des fonctions du module sont utilisées dans
le programme.

Par exemple, pour importer l’ensemble du module math :

>>> from math import *


>>> cos (3 * pi / 4)
-0.7071067811865475
La librairie standard : Utilisation des fonctions standard

Une autre raison de ne pas importer la totalité des modules est d'éviter
d'éventuels conflits qui peuvent se produire lorsque des fonctions intégrées
dans les différents modules importés portent le même nom.

Par exemple, la fonction sqrt est définie à la fois dans le module math, mais
aussi dans le module cmath (nombres complexes) :

>>> from cmath import *


>>> from math import *
>>> print('Racine carrée de -4 =', sqrt(-4))
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
ValueError: math domain error

La fonction sqrt étant définie dans les deux modules (cmath et math), c’est
la fonction qui se trouve dans le dernier module importé qui est utilisée…
La librairie standard : Utilisation des fonctions standard

Pour résoudre ce problème, Python permet d’importer juste le contenu d’un


module avec l'instruction import suivie du nom du module à importer.

Ensuite, lorsqu'on voudra utiliser une fonction du module, il faudra faire


précéder son nom de celui du module et de l'opérateur d'accès (.) :

>>> import cmath


>>> import math
>>> print('Racine carrée de -4 =', cmath.sqrt(-4))
2j

Il est maintenant explicite que la fonction sqrt appelée est celle provenant du
module cmath, et il n'y a donc plus d'erreurs d'exécution.
La librairie standard : Utilisation des fonctions standard

Une fois qu’une fonction est importée, il est possible d’obtenir de l’aide sur
l’utilisation de celle-ci grâce à la l’instruction help.

>>> from math import sqrt

>>> help(sqrt)
Help on built-in function sqrt in module math:

sqrt(...)
sqrt(x)

Return the square root of x.


Les sous-programmes

Notion de sous-programme

La librairie standard

Les fonctions originales

Les procédures

La description d’une fonction


Les fonctions originales

L’écriture d’un programme nécessite souvent l’utilisation de fonctions


spécifiques qui ne sont pas fournies dans la librairie standard du langage
utilisé.

Dans ce cas, il sera nécessaires d’écrire nous même ces fonctions.

une fonction se définit par trois critères :

• une entrée : valeur(s) fournie(s) à la fonction. Ces valeurs sont appelées


les paramètres (ou les arguments) de la fonction.

• un corps : contient le code source de la fonction, qui sert à calculer un


résultat.

• une sortie : valeur(s) renvoyée(s) par la fonction, une fois les calculs
terminés.
Les fonctions originales

La syntaxe Python pour la définition d'une fonction est la suivante :

def nomDeLaFonction(liste de paramètres) : en-tête de la fonction


...
bloc d'instructions Corps de la fonction
...

À noter le décalage vers la droite du corps de la fonction par rapport à l’en-


tête. Il est obligatoire en Python et marque un bloc d'instructions. Ce décalage
se nomme indentation.

Les paramètres reçus par la fonction sont appelés paramètres formels.

Ces paramètres sont stockés dans la mémoire de l’ordinateur lors de l’appel


de la fonction et seront effacés dès la fin de son d’exécution.

Les paramètres formels ne sont accessibles qu’à l’intérieur du corps de la


fonction… Ce sont des variables locales.
Les fonctions originales : Notre première fonction

Soit une fonction, appelée triple, qui ne reçoit qu’un seul paramètre nommé
valeur.

Le rôle de cette fonction sera de calculer puis renvoyer le triple de la valeur


reçue en paramètre.

En Python, cette fonction peut s’écrire de la façon suivante :

def triple(valeur) :
resultat = valeur * 3
return resultat

Ou plus directement :

def triple(valeur) :
return valeur * 3
Les fonctions originales : Appel de fonction

Pour pouvoir utiliser une fonction une fois qu'elle est écrite, il faut l'appeler.

L'appel à une fonction se fait simplement en écrivant son nom et en précisant


des valeurs pour les paramètres.

Lors de l’appel d’une fonction, les valeurs des paramètres réels (ou arguments)
sont affectées aux paramètres formels.

paramètre formel

def triple(valeur) :
resultat = valeur * 3
return resultat

entier = 3

triple_entier = triple(entier)

paramètre réel
Les fonctions originales : Appel de fonction

Python utilise l’inférence de type lors de la création des variables, ceci est
également vrai lors du passage des arguments entre fonction appelante et
fonction appelée.

Au moment de l’appel, le type d’un paramètre formel sera identique à celui


du paramètre réel associé.

Les 3 types de variable int, float et string peuvent être utilisés en paramètre
de la fonction triple du fait de la tolérance de l'opérateur * dans Python.

def triple(valeur) :
resultat = valeur * 3
return resultat
Les fonctions originales : Appel de fonction

La fonction triple peut s’appliquer sur des valeurs entières :

paramètre formel de type int


def triple(valeur) :
resultat = valeur * 3
return resultat

>>> triple(3)
9
paramètre réel de type int
Les fonctions originales : Appel de fonction

La fonction triple peut aussi s’appliquer sur des valeurs réelles :

paramètre formel de type float


def triple(valeur) :
resultat = valeur * 3
return resultat

>>> triple(1.5)
4.5
paramètre réel de type float
Les fonctions originales : Appel de fonction

La fonction triple peut aussi s’appliquer sur des chaînes de caractères :

paramètre formel de type str


def triple(valeur) :
resultat = valeur * 3
return resultat

>>> triple('Oh!')
'Oh!Oh!Oh!'
paramètre réel de type str

Appliquée sur une chaine de caractères, cette fonction réalise l’opération de


duplication.
Les fonctions originales : Appel de fonction

Les exemples d'appels ci-dessous présentent différentes façon de préciser


une valeur pour les paramètres d’une fonction :

>>> triple(3) paramètre = valeur littérale


9

>>> entier = -10


>>> triple(entier) paramètre = variable
-30

>>> reel = 4.5


>>> triple(entier + reel)
-16.5

>>> triple(-10 + reel)


-16.5
Les fonctions originales : Appel de fonction

Les exemples d'appels ci-dessous présentent différentes façon de préciser


une valeur pour les paramètres d’une fonction :

>>> triple(triple(3)) paramètre = fonction


27
Les fonctions originales : Appel de fonction

Une fonction peut avoir plusieurs paramètres.

Dans ce cas, les paramètres réels doivent être passés à la fonction dans le
même ordre que celui des paramètres formels correspondant à la définition
de la fonction.

>>> def division(a, b) :


resultat = a / b
return resultat

>>> valeur_a = 8
>>> valeur_b = 2

>>> division(valeur_a, valeur_b)


4.0

>>> division(valeur_b, valeur_a)


0.25
Les fonctions originales : Fonctions non paramétrées

Dans certains cas, vous pouvez aussi ne transmettre aucun paramètre à la


fonction.

Par exemple, on pourrait imaginer concevoir une fonction simulant un dé à six


faces. Cette fonction n'a pas besoin de paramètre en entrée car la valeur de
sortie ne produit que des nombres aléatoires compris entre 1 et 6.

>>> from random import randint

>>> def de_six_faces() :


return randint(1, 6)

>>> de_six_faces()
5

>>> de_six_faces()
2

Les parenthèses sont obligatoires, même en l'absence de paramètre, que ce


soit pour la définition ou pour l'appel de la fonction.
Les fonctions originales : Sortie de fonction

Une fonction (au sens strict) doit toujours renvoyer une valeur lorsqu'elle se
termine. Ce qui signifie qu'une fonction doit impérativement contenir une
instruction return dans son code.

Une fonction peut s'utiliser à la droite du signe égal (qui représente l'affectation)
dans des expressions.

Pour que l'appel à une fonction soit utile, la valeur qu'elle renvoie doit être soit
affectée à une variable, soit utilisée directement dans une expression plus ou
moins complexe ou encore affichée à l’écran :

>>> valeur = triple(3)

>>> a = 2
>>> coef = 5 + triple(a) / 2

>>> print(a, 'multiplié par 3 =', triple(a))


2 multiplié par 3 = 6
Prédicat ou fonction booléenne

Un prédicat est une fonction qui retourne une valeur booléenne (True ou False).

La valeur à retourner peut être déterminée sans passer nécessairement par une
structure conditionnelle (if… else…).

def est_divisible_par(nb1, nb2) : def est_divisible_par(nb1, nb2) :

if (nb1 % nb2) == 0 : return (nb1 % nb2) == 0


resultat = True
else :
resultat = False

return resultat
Les sous-programmes

Notion de sous-programme

La librairie standard

Les fonctions originales

Les procédures

La description d’une fonction


Les fonctions originales : Procédures

Dans certains cas, une fonction ne renvoie pas de valeur. On parle alors de
procédure.

Une procédure génère un effet de bord qui peut être la modification d’une
valeur en mémoire, l’affichage d’un message à l’écran… (modification de
l’environnement d’exécution).

Du fait qu’elle ne renvoie pas de valeur, une procédure ne peut pas être
utilisée dans une expression…
Les fonctions originales : Procédures

Les procédures sont généralement utilisées pour afficher des messages à


l’écran.

>>> def bonjour(prenom) :


print("Bienvenue en L1", prenom, "!")
return

>>> bonjour("John")
Bienvenue en L1 John !

Comme une procédure ne renvoie pas de valeur, l’instruction return n’est pas
obligatoire…

>>> def bonjour(prenom) :


print("Bienvenue en L1", prenom, "!")

>>> bonjour("John")
Bienvenue en L1 John !
Les fonctions originales : Procédures

L'utilisation d'une procédure à la droite du signe égal dans des expressions ne


déclenchera pas d'erreur, mais la variable affectée recevra la valeur None.

L'affectation d'une procédure à une variable n’a donc pas de sens :

>>> def bonjour(prenom) :


print("Bienvenue en L1", prenom, "!")
return

>>> x = bonjour("John")
Bienvenue en L1 John !

>>> x
None

>>> print(bonjour("John"))
Bienvenue en L1 John !
None
Les fonctions originales : Procédures

Prenons l’exemple de la procédure non paramétrée double qui multiplie par 2


la valeur de la variable n.

>>> n = 3

>>> m = 2 + double()
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'

Le message d’erreur indique que la procédure double ne retourne rien


(NoneType) et que par conséquent elle ne peut pas être utilisée comme
opérande de l’addition.

En Python, une procédure est considérée comme une fonction qui ne retourne
rien (None).
Les sous-programmes

Notion de sous-programme

La librairie standard

Les fonctions originales

Les procédures

La description d’une fonction


Les fonctions originales : Description d’une fonction

Une fonction a pour objectif d'être réutilisée dans un programme, soit par son
auteur, soit par un(des) autre(s) programmeur(s)/utilisateur(s).

Il est donc important de décrire toutes les fonctions sinon elles seront
difficilement réutilisables…

En Python, la description d’une fonction comprend 3 parties :

• La documentation : docstring

• Les contraintes (ou conditions) d’utilisation : CU

• Les tests de fonction : doctests


Les fonctions originales : Description d’une fonction : docstring

La documentation d’une fonction, aussi appelée docstring, contient :

• une chaîne de caractères expliquant le rôle de la fonction.

• la liste des différents paramètres, avec comme en-tête


:param nom_du_param:, suivi des types possibles du paramètre
entre parenthèses et d'une brève description.

• les valeurs de sortie, avec comme en-tête :return:, suivi des


types possibles de la sortie et d'une brève description.

La documentation d'une fonction doit être placée juste en dessous de l’entête


de la fonction. Elle est représentée par une chaine de caractères comprise
entre deux délimiteurs """ (triples guillemets).

Les termes écrits entre : sont des balises, ils identifient des informations
particulières de la docstring. Il est important de respecter cette syntaxe car
elle est utilisée par le générateur de documentation Sphinx pour extraire la
documentation et la mettre en forme dans des fichiers PDF ou HTML.
Les fonctions originales : Description d’une fonction : docstring

Exemple de docstring

Soit la fonction nommée plus_grande_racine qui renvoie la plus grande des


deux racines d’un polynôme du second degré ax2+bx+c dont on passe les
coefficients en paramètre.

Cette plus grande racine est évidemment égale à :

from math import sqrt

def plus_grande_racine (a, b, c) :


delta = b**2 - 4*a*c
x1 = (-b + sqrt(delta)) / (2*a)
x2 = (-b - sqrt(delta)) / (2*a)
return max(x1, x2)
Les fonctions originales : Description d’une fonction : docstring

from math import sqrt

def plus_grande_racine (a, b, c) :


"""
Renvoie la plus grande racine du polynome
de second degré a(x*x) + bx + c
:param a: (int or float) coefficient a (associé à x*x)
:param b: (int or float) coefficient b (associé à x)
:param c: (int or float) coefficient c (constant)
:return: (float) la plus grande racine du polynome
"""
delta = b**2 - 4*a*c
x1 = (-b + sqrt(delta)) / (2*a)
x2 = (-b - sqrt(delta)) / (2*a)
return max(x1, x2)
Les fonctions originales : Description d’une fonction : docstring

Les docstrings permettent à un programmeur de savoir ce que font les


fonctions sans avoir à lire et comprendre leur code.

Les docstrings sont lues par Python, en utilisant la fonction help :

>>> help(plus_grande_racine)
Help on function plus_grande_racine in module __main__:

plus_grande_racine(a, b, c)
Renvoie la plus grande racine du polynome
de second degré a(x*x) + bx + c
:param a: (int or float) coefficient a (associé à x*x)
:param b: (int or float) coefficient b (associé à x)
:param c: (int or float) coefficient c (constant)
:return: (float) la plus grande racine du polynome
Les fonctions originales : Description d’une fonction : CU

Les paramètres que l’on passe à une fonction peuvent être soumis à des
contraintes à respecter si l’on veut que le calcul se déroule sans incident.

Lorsqu'un incident se produit, il faut que la fonction puisse avertir l'utilisateur,


sur la provenance du problème. Dans ce cas, on dit que la fonction déclenche
une exception.

Par exemple, la racine carrée d’un nombre réel négatif n’est pas définie. Voici
ce qu'il se passe lorsque l'on fournit un nombre négatif à la fonction sqrt en
Python :

>>> from math import sqrt


>>> sqrt(-1)
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
ValueError: math domain error

Les nombres que l’on passe à la fonction sqrt doivent être positifs ou nuls.
C’est une contrainte d’utilisation (CU) de cette fonction.
Les fonctions originales : Description d’une fonction : CU

Évidemment, la fonction plus_grande_racine que nous avons écrite hérite de


la contrainte de la fonction sqrt puisqu’elle l’utilise.

Tout appel à cette fonction avec les coefficients d’un polynôme dont le
discriminant est négatif déclenchera la même erreur.

C’est ainsi le cas pour le polynôme x2+x+1 :

delta = 12 – 4*1*1 = -3  sqrt(delta) = sqrt(-3) !!!

>>> plus_grande_racine(1, 1, 1)
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
File "<pyshell>", line 13, in plus_grande_racine
ValueError: math domain error

Il est important, dans ce cas, de vérifier que b2 − 4ac ≥ 0


Les fonctions originales : Description d’une fonction : CU

Une fonction peut avoir à respecter plusieurs contraintes.

C'est le cas de la fonction plus_grande_racine qui possède une deuxième


contrainte d’utilisation.

En effet, il est important de vérifier la valeur de a, car le calcul des racines


nécessite la division par 2*a qui déclenche une exception dans le cas où a
est égal à 0.

>>> plus_grande_racine(0, 1, 1)
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
File "<pyshell>", line 13, in plus_grande_racine
ZeroDivisionError: float division by zero

Il est important, dans ce cas, de vérifier que a ≠ 0


Les fonctions originales : Description d’une fonction : CU

Une utilisation normale de la fonction plus_grande_racine nécessite donc le


respect de deux contraintes :

• b2 − 4ac ≥ 0
•a≠0

Pour donner plus de détails à l'utilisateur, il faut renseigner les contraintes


d'utilisations dans la description de la fonction.

Cela se formalise par une ligne commençant par CU, suivie des différentes
contraintes d'utilisation.
Les fonctions originales : Description d’une fonction : CU

def plus_grande_racine (a, b, c) :


"""
Renvoie la plus grande racine du polynome
de second degré a(x*x) + bx + c
:param a: (int or float) coefficient a (associé à x*x)
:param b: (int or float) coefficient b (associé à x)
:param c: (int or float) coefficient c (constant)
:return: (float) la plus grande racine du polynome

CU : (a != 0) and (b**2 - 4*a*c >= 0)


"""
delta = b**2 - 4*a*c
x1 = (-b + sqrt(delta)) / (2*a)
x2 = (-b - sqrt(delta)) / (2*a)
return max(x1, x2)
Les fonctions originales : Description d’une fonction : doctests

Lors de l'écriture des spécifications d'une fonction, il est bon de réfléchir à


plusieurs cas d'utilisation (variation des paramètres) et aux valeurs qui
seront renvoyées pour chacun de ces cas.

Comme vu précédemment, il faut aussi anticiper les contraintes d'utilisation.

Afin de s'assurer que le code de la fonction respecte bien les spécifications, il


est possible de mettre en place des tests qui vérifient que la valeur renvoyée
lors de l'appel à la fonction avec un jeu de paramètres donné correspond
bien à ce qui est attendu.

On appelle cette vérification les tests unitaires.


Les fonctions originales : Description d’une fonction : doctests

En python, une syntaxe particulière permet d'effectuer facilement ces tests,


en les incluant dans la spécification de la fonction.

Les lignes contenant des instructions Python devant être considérées comme
doctest doivent être incluses dans la spécification et commencer par >>> qui
est l'invite de commande de l'interpréteur Python.

Sur la ligne suivante, le résultat attendu de l'expression doit être écrit tel qu'il
apparaît dans l'interpréteur.
Les fonctions originales : Description d’une fonction : doctests

def plus_grande_racine (a, b, c) :


"""
Renvoie la plus grande racine du polynome
de second degré a(x*x) + bx + c
:param a: (int or float) coefficient a (associé à x*x)
:param b: (int or float) coefficient b (associé à x)
:param c: (int or float) coefficient c (constant)
:return: (float) la plus grande racine du polynome

CU : (a != 0) and (b**2 - 4*a*c >= 0)

Exemples :
>>> plus_grande_racine (1, 1, -6)
2.0
>>> plus_grande_racine (3, 8, 5)
-1.0
"""
delta = b**2 - 4*a*c
x1 = (-b + sqrt(delta)) / (2*a)
x2 = (-b - sqrt(delta)) / (2*a)
return max(x1, x2)
Les fonctions originales : Description d’une fonction : doctests

Une fois les tests rédigés, ils peuvent être lancés automatiquement à l’aide de
la fonction testmod intégrée dans le module nommé doctest.

L’instruction est la suivante :

>>> import doctest


>>> doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE |
doctest.ELLIPSIS, verbose = True)

La fonction testmod cherche dans la spécification des fonctions, tous les


exemples (reconnaissables à la présence de l’invite >>>), et vérifie que la
fonction documentée satisfait bien ces exemples.

Les options passées en paramètre de testmod permettent :


• de ne pas prendre en compte les espaces ou retours à la ligne
supplémentaires ajoutés dans la valeur renvoyée par la fonction (pour
des commodités de lecture),
• d'utiliser ... (points de suspension = ellipsis marker) pour éviter d'écrire
les messages d'erreur (ou autre) complets,
• et demande à ce que les résultats des tests soit décrits en détails.
Les fonctions originales : Description d’une fonction : doctests

>>> import doctest


>>> doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE |
doctest.ELLIPSIS, verbose = True)
Trying:
plus_grande_racine (1, 1, -6)
Expecting:
2.0
ok
Trying:
plus_grande_racine (3, 8, 5)
Expecting:
-1.0
ok
1 items had no tests:
__main__
1 items passed all tests:
2 tests in __main__.plus_grande_racine
2 tests in 2 items.
2 passed and 0 failed.
Test passed.
TestResults(failed=0, attempted=2)
Les fonctions originales : Description d’une fonction : doctests
Ajoutons un exemple faux à plus_grande_racine et observons ce qu'il se
passe lorsque nous lançons l'analyse automatique des tests.

def plus_grande_racine (a, b, c) :


"""
Renvoie la plus grande racine du polynome
de second degré a(x*x) + bx + c
:param a: (int or float) coefficient a (associé à x*x)
:param b: (int or float) coefficient b (associé à x)
:param c: (int or float) coefficient c (constant)
:return: (float) la plus grande racine du polynome

CU : (a != 0) and (b**2 - 4*a*c >= 0)

Exemples :
>>> plus_grande_racine (1, 1, -6)
2.0
>>> plus_grande_racine (3, 8, 5)
-1.0
>>> plus_grande_racine (2, 1, -6)
2.0
"""
delta = b**2 - 4*a*c
x1 = (-b + sqrt(delta)) / (2*a)
x2 = (-b - sqrt(delta)) / (2*a)
return max(x1, x2)
Les fonctions originales : Description d’une fonction : doctests

>>> doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS,


verbose = True)
Trying:
plus_grande_racine (1, 1, -6)
Expecting:
2.0
ok
Trying:
plus_grande_racine (3, 8, 5)
Expecting:
-1.0
ok
Trying:
plus_grande_racine (2, 1, -6)
Expecting:
2.0
**********************************************************************
File "__main__", line 20, in __main__.plus_grande_racine
Failed example:
plus_grande_racine (2, 1, -6)
Expected:
2.0
Got:
1.5
1 items had no tests:
__main__
**********************************************************************
Les fonctions originales : Description d’une fonction : doctests

1 items had failures:


1 of 3 in __main__.plus_grande_racine
3 tests in 2 items.
2 passed and 1 failed.
***Test Failed*** 1 failures.
TestResults(failed=1, attempted=3)

Lorsqu’il y a de tels échecs, cela invite le programmeur à vérifier son


programme... ou bien les exemples de sa documentation, comme c’est
le cas ici.
Les fonctions originales : Description d’une fonction : doctests

Enfin, certains tests peuvent servir à vérifier le comportement en cas de


passage de paramètres hors des contraintes d'utilisation.

Dans ce cas, l'interpréteur affiche un message d'erreur qui peut être assez
long.

Heureusement, nous n'avons pas besoin d'écrire le message en entier dans


le doctest, il suffit de recopier la première ligne du message, mettre une
ligne avec ... puis la dernière ligne.

Ajoutons un exemple qui produit une erreur lors de l’exécution de


plus_grande_racine et observons ce qu'il se passe lorsque nous lançons
l'analyse automatique des tests.
Les fonctions originales : Description d’une fonction : doctests

def plus_grande_racine (a, b, c) :


"""
Renvoie la plus grande racine du polynome
de second degré a(x*x) + bx + c
:param a: (int or float) coefficient a (associé à x*x)
:param b: (int or float) coefficient b (associé à x)
:param c: (int or float) coefficient c (constant)
:return: (float) la plus grande racine du polynome

CU : (a != 0) and (b**2 - 4*a*c >= 0)

Exemples :
>>> plus_grande_racine (1, 1, -6)
2.0
>>> plus_grande_racine (3, 8, 5)
-1.0
>>> plus_grande_racine (1, 1, 1)
Traceback (most recent call last): delta = 1**2 – 4*1*1 = -3
... sqrt(-3)  erreur
ValueError: math domain error
""" >>> from math import sqrt
delta = b**2 - 4*a*c >>> sqrt(-3)
x1 = (-b + sqrt(delta)) / (2*a) Traceback (most recent call last):
x2 = (-b - sqrt(delta)) / (2*a) File "<pyshell>", line 1, in <module>
return max(x1, x2) ValueError: math domain error
Les fonctions originales : Description d’une fonction : doctests
>>> doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE |
doctest.ELLIPSIS, verbose = True)
Trying:
plus_grande_racine (1, 1, -6)
Expecting:
2.0
ok
Trying:
plus_grande_racine (3, 8, 5)
Expecting:
-1.0
ok
Trying:
plus_grande_racine (1, 1, 1)
Expecting:
Traceback (most recent call last):
...
ValueError: math domain error
ok
1 items had no tests:
__main__
1 items passed all tests:
3 tests in __main__.plus_grande_racine
3 tests in 2 items.
3 passed and 0 failed.
Test passed.
TestResults(failed=0, attempted=3)
Les fonctions originales : Description d’une fonction

Dans le cas d'une fonction renvoyant un nombre aléatoire, il est


impossible de placer dans la spécification un exemple donnant le résultat
d’un appel à cette fonction puisque les valeurs qu’elles renvoient sont
imprévisibles.
Les fonctions originales : Description d’une fonction

Les conventions suivantes doivent être respectées :

• la phrase de description doit toujours commencer par "Renvoie" pour


les fonctions et par "Affiche" dans le cas d'une procédure.

• les types des paramètres et de la valeur renvoyée, précisés entre


parenthèses, doivent être exhaustifs.

Certains opérateurs ou certaines fonctions du langage Python sont


tolérant vis-à-vis des types. Si votre fonction tolère plusieurs types pour
un paramètre, il faut le préciser en écrivant la liste des types séparés
par or ou en écrivant all si vous pensez que tous les types sont tolérés.
Attention, vous ne connaissez pas encore tous les types existants en
Python.

• la balise :return: est obligatoire. Dans le cas d'une procédure, il faut


écrire :return: None.

• la ligne CU est obligatoire. S'il n'y a pas de contraintes d'utilisation, il


faut écrire CU : aucune.
Les fonctions originales : Description d’une fonction

Avant d'écrire toute fonction vous devez écrire sa spécification complète


avec la phrase de description, les paramètres et la valeur renvoyée, les
conditions d'utilisation et le doctest avec au moins 3 tests différents (sauf
cas particuliers).

En TP, aucune réponse ne sera fournie à toute question portant sur la


programmation d’une fonction non documentée…

Toute fonction non documentée dans un compte-rendu de TP sera


considérée comme inexistante… et ne sera donc pas corrigée.

Vous aimerez peut-être aussi