Appel et déclaration de sous-programme


Reprenons le projet Peinture du cours Première Notion.

Rappelons que ce programme permet de calculer la surface à peindre d'une pièce rectangulaire connaissant les dimensions de la pièce (longueur, largeur), le nombre de fenêtres et le nombres de portes.

Cet exemple nous avait justement servi à illustrer le principe du découpage d'un traitement en sous-traitements. Lorsqu'un calcul est trop complexe, on a intérêt à le découper en plusieurs calculs intermédiaires et sauvegarder les résultats intermédiaires dans des variables.

Cela nous avait donné le code suivant.

Appel de sous-programmes

Nous avons ici un exemple de code qui peut très facilement se réécrire à l'aide de sous-programmes de la manière suivante (la partie déclaration des sous-programmes a été ommise en un premier temps pour ne pas surcharger l'exemple) :

# Constantes
    
LARGEUR_FENETRE = 1.4
HAUTEUR_FENETRE = 1.2
LARGEUR_PORTE = 0.9
HAUTEUR_PORTE = 2.1
HAUTEUR_MUR = 2.5

# Declaration des sous-programmes

......

# Lecture des donnees

LargeurPiece = float (input ("Largeur de la piece : "))
LongueurPiece = float (input ("Longueur de la piece : "))
NFenetre = int (input ("Nombre de fenetres : "))
NPorte = int (input("Nombre de portes : "))

# Traitement des donnees

CalculerSurfaceDesMurs ()
CalculerSurfaceDesFenetres () 
CalculerSurfaceDesPortes () 
CalculerSurface_A_Peindre ()

# Affichage du resultat

print ("Surface a peindre : "+ str(SurfacePeinture))
 

Les instructions en blanc sont des appels de sous-programme.

Déclaration de sous-programme

Les instructions permettant de calculer les surfaces (murs, fenêtres, portes, surface à peindre) sont à présent contenues dans des sous-programmes situés juste après la définition des constantes. Voici leur code:

....
# Declaration des sous-programmes

def  CalculerSurfaceDesMurs () :
    global SurfaceDesMurs
    SurfaceDesMurs =  \
    2 * (LargeurPiece * HAUTEUR_MUR + \
         LongueurPiece * HAUTEUR_MUR)
    
def CalculerSurfaceDesFenetres () :
    global  SurfaceDesFenetres
    SurfaceDesFenetres = \
    NFenetre * LARGEUR_FENETRE * HAUTEUR_FENETRE
    
def CalculerSurfaceDesPortes () :
    global SurfaceDesPortes
    SurfaceDesPortes = \
    NPorte * LARGEUR_PORTE * HAUTEUR_PORTE 

def CalculerSurface_A_Peindre () :
    global SurfacePeinture
    SurfacePeinture = \
    SurfaceDesMurs  - SurfaceDesFenetres - SurfaceDesPortes    

# Lecture des donnees
...

Nous avons ici quatre déclaration de sous-programmes.

De manière générale, une déclaration de sous-programme s'écrit de la manière suivante:

def  Nom du sous-programme ( liste des paramètres ) :
    Bloc d'instructions
    

La première ligne de la déclaration s'appelle l'entête. Le bloc d'instruction situé juste après l'entête est le corps du sous-programme.

Précisons également que les quatre exemples de sous-programmes présentés ici sont très particuliers:

Avec les sous-programmes, les instructions d'un fichier source python peuvent être classées en deux catégories:

Toute instruction python appartient donc soit au programme principal, soit à un sous-programme.

Exécution et retour d'un sous-programme. Cas particulier des procédures

Un sous-programme ne s'éxécute que lorsqu'il est appelé. Pour une procédure, cela signifie qu'il existe quelque part dans le code une instruction d'appel de ce sous-programme. Dans notre exemple, ce sont les quatre instructions en blanc dans le corps du programme principal.

Voyons un peu comment s'exécute le programme.

L'exécution d'un programme Python démarre toujours à la première instruction du programme principal.

Dans notre exemple, ce sera l'instruction:

 LARGEUR_FENETRE = 1.4 

Nous sommes dans la définition des constantes.

Après avoir exécuté les quatres affectations définissant les quatre constantes suivantes, l'interpréteur rencontre une déclaration de sous-programme.

def  CalculerSurfaceDesMurs () :
    global SurfaceDesMurs
    SurfaceDesMurs =  \
    2 * (LargeurPiece * HAUTEUR_MUR + \
         LongueurPiece * HAUTEUR_MUR) 

A ce stade les instructions contenues dans le sous-programme ne sont pas exécutées. L'interpréteur se contente d'enregistrer la définition du sous-programme en mémoire. Il sait à présent qu'il existe un sous-programme nommé CalculerSurfaceDesMurs et il connait exactement les instructions qu'il contient, mais il ne les exécute pas.

Même principe pour les trois déclarations de sous-programme qui suivent.

Après avoir exécuté les instructions de lectures des données, l'interprèteur rencontre une instruction d'appel du sous-programme CalculerSurfaceDesMurs.

Cet appel va déclencher l'exécution de ce sous-programme. C'est à dire que l'interpréteur va à présent exécuter les instructions se trouvant à l'intérieur du corps du sous-programme CalculerSurfaceDesMurs. En l'occurence, il s'agit de l'instruction:

 SurfaceDesMurs =  \
      2 * (LargeurPiece * HAUTEUR_MUR + \ 
           LongueurPiece * HAUTEUR_MUR); 

Lorsque l'exécution de cette procédure est terminée, il y a un retour de sous-programme , c'est à dire que l'exécution continue à l'instruction qui suit l'appel du sous-programme venant d'être appelé. Dans notre cas, le retour se fait donc à l'instruction d'appel du sous-programme CalculerSurfaceDesFenetres.

Ce sous-programme sera donc exécuté et au retour, ce sera l'appel du sous-programme CalculerSurfaceDesFenetres, etc ... jusqu'à ce que tous les appels de sous-programme contenus dans le corps du programme principal soient exécutés. Et finalement, l'exécution du programme se terminera par l'affichage du résultat.

Le mécanisme de retour de sous-programme que nous avons décrit ici s'applique à tous les appels de procédure: après l'exécution d'une procédure, l'ordinateur revient automatiquement à l'instruction qui suit l'appel. Pour les fonctions, nous verrons que le mécanisme de retour est légèrement différent.

Emplacement des déclarations dans le code

Il est possible de déclarer des sous-programmes n'importe où dans le code (même à l'intérieur d'un autre sous-programme !). Mais il y a par contre un principe qui doit être respecté: l'interprèteur doit nécessairement connaitre la fonction au moment où elle est appelée. Autrement dit, la déclaration doit précéder l'appel.

Par ailleurs, notez bien que dans un programme contenant des sous-programmes, la première instruction exécutée ne correspond pas forcément, comme dans notre exemple, à la première ligne de code du fichier. Rappelons qu'il s'agit de la première instruction du programme principal. Cette instruction sera appelée le point d'entrée dans la suite du cours.