Les fonctions

Les fonctions

Voici des instructions qui permettent d'afficher le carré d'un nombre

>>>a=3
>>>b=a**2
>>>b
9

Si, nous avons également besoin du carré de 5 et 7... on pourrait copier-coller les instructions précédentes et afficher le résultat. Mais il y a mieux à faire : regrouper ces instructions dans un bloc sous la forme d'une fonction, a devenant un paramètre de la fonction.

Une fonction permet donc d'éviter d'avoir à écrire plusieurs fois le même code lorsqu'un même traitement doit être mis en oeuvre à plusieurs endroits dans le programme.

En Python une fonction est définie par l'instruction composée def suivie du nom de la fonction et se termine obligatoirement par deux points : puis le bloc d'instructions qu'il ne faut pas oublier d'indenter.

Une fonction est utilisée comme une instruction quelconque. Dans le corps d'un programme, un appel de fonction est constitué du nom de la fonction suivi de parenthèses

La notion de fonction en informatique relève du même concept qu'une fonction mathématique, c'est-à-dire qu'on définit une fonction puis on l'applique à différentes valeurs.

# -*- coding: utf-8 -*-
def carree(a) :
	""" 
	Fonction permettant de renvoyer le carré du nombre a qui est en paramètre
	"""
	return a**2 # renvoye l'image de a par la fonction carree

Pour écrire le programme ci-dessus dans la zone de Saisie de programme de EduPython, on clique sur Fichier, Nouveau puis Nouveau Module Python comme l'indique la figure ci-dessous :

Fonction

ou bien, on clique sur Nouveau Fichier, l'icônePython 3.4, puis Créer comme l'indique la figure ci-dessous :

Fonction

Une fois la fonction est définie dans la zone de Saisie de programme de EduPython, on ne doit pas oublier de l'enregistrer dans le dossier qui convient en lui donnant un nom :

Fonction

Après l'enregistrement, pour utiliser cette fonction, on l'appelle par l'instruction carree(3) en utilisant la console pour afficher le carré de 3 mais il ne faut pas oublier d'exécuter le programme en utilisant l'icône "flèche verte" avant d'utiliser la console, sinon la fonction carree ne sera pas reconnue comme l'indique la figure ci-dessous :

Fonction

Lorsqu'on définit la fonction carree(), a est appelé paramètre de la fonction. Quant on appelle la fonction avec une valeur explicite pour a, comme dans carree(3), on dira plutôt que 3 est un argument de la fonction.

En appelant la fonction carree() d'argument 3, on obtient 9 :

>>> carree(3)
9
>>>

Quelques remarques.

  • La fonction est auto-documentée par un texte entre """ et """ (c'est ce que l'on appelle le docstring de la fonction).

    Le docstring d'une fonction permet de donner des informations sur la fonction, le lien entre les entrées et la sortie (en d'autres termes, le rôle de la fonction).

    Toutes vos fonctions devront commencer ainsi par un docstring.

  • Quant on saisit dans la console, après l'exécution de la fonction, l'instruction help(nom de la fonction), python affiche le docstring de la fonction ce qui nous permet d'avoir des informations sur la fonction en cas d'oubli.
>>> help(carree)
Help on function carree in module __main__:

carree(x)
    Fonction permettant de renvoyer le carré
    du nombre x qui est en paramètre

>>>
  • La fonction se termine avec une instruction return. Ce qui suit le return est l'image des entrées par la fonction. Dès que la fonction rencontre un return, elle renvoie ce qui suit le return et stoppe son exécution.
  • Un autre exemple.

    On considère le programme suivant dressant la table du 8 :

    
    n=8
    for k in range(1,11) :
    	print( '{}*{} = {}'.format(n,k,n*k) )
    
    qui affiche :
    8*1 = 8
    8*2 = 16
    8*3 = 24
    8*4 = 32
    8*5 = 40
    8*6 = 48
    8*7 = 56
    8*8 = 64
    8*9 = 72
    8*10 = 80

    Si, dans le même programme, nous avons également besoin de la table du 9, du 7... on pourrait copier-coller le code précédent. Mais il y a mieux à faire : nommer ce code en le transformant en fonction, n devenant un paramètre de la fonction.

    
    def table(n) : # fonction nommée table et présentant un paramètre : n
    	for k in range(1,11) :
    			print( '{}*{} = {}'.format(n,k,n*k) )
    	print() # pour laisser une ligne vide en fin de table
    			
    ## ce qui suit constitue des appels à la fonction table. ##
    ## Lorsqu'on appelle une fonction pour la mettre en oeuvre, ##
    ## on donne une valeur explicite aux paramètres de la fonction. ##
    
    table(8) # appel de la fonction table avec l'argument 8
    table(7) # appel de la fonction table avec l'argument 7
    

    L'affichage obtenu :

    8*1 = 8
    8*2 = 16
    8*3 = 24
    8*4 = 32
    8*5 = 40
    8*6 = 48
    8*7 = 56
    8*8 = 64
    8*9 = 72
    8*10 = 80
    
    7*1 = 7
    7*2 = 14
    7*3 = 21
    7*4 = 28
    7*5 = 35
    7*6 = 42
    7*7 = 49
    7*8 = 56
    7*9 = 63
    7*10 = 70

    Une fonction peut présenter plusieurs paramètres.

    Si l'on veut pouvoir par exemple afficher la table de 1*n à 12*n (au lieu de l'affichage de 1*n à 10*n), on peut ajouter un paramètre marquant le dernier coefficient voulu.

    
    def table(n, fin) : # fonction nommée table et présentant deux paramètres : n et fin
    	for k in range(1,fin+1) :
    			print( '{}*{} = {}'.format(n,k,n*k) )
    	print()  
    	
    	
    			
    ##  appels : ##
    table(8, 12)  
    table(7, 15)  
    

    Ce qui donne :

    8*1 = 8
    8*2 = 16
    8*3 = 24
    8*4 = 32
    8*5 = 40
    8*6 = 48
    8*7 = 56
    8*8 = 64
    8*9 = 72
    8*10 = 80
    8*11 = 88
    8*12 = 96
    
    7*1 = 7
    7*2 = 14
    7*3 = 21
    7*4 = 28
    7*5 = 35
    7*6 = 42
    7*7 = 49
    7*8 = 56
    7*9 = 63
    7*10 = 70
    7*11 = 77
    7*12 = 84
    7*13 = 91
    7*14 = 98
    7*15 = 105
    

    Image par une fonction

    Les deux fonctions définies ci-dessus ne font pas correspondre d'image à n (ou au couple (n, fin) ). Ces fonctions se contentent d'afficher à l'écran un message. Dans certains langages, on parle plutôt de procédure que de fonction dans une telle situation.

    A quoi ressemblerait une fonction faisant correspondre une image à l'entier n ?

    Comme un message est avant tout une chaîne de caractères, on pourrait décider ici de ne pas afficher cette chaîne, mais d'en faire l'image de n.

    
    def table(n) : 
    	ch='' # initialisation de ch à la chaîne vide 
    	for k in range(1,11) :
    			ch+='{}*{} = {}'.format(n,k,n*k)+'\n'
    	return ch  # ch sera l'image de n 
    	
    	
    			
    ##  appels : ##
    table(8)  
    

    Lancez ce programme. Que se passe-t-il ?

    Rien. Ou plutôt rien de visible. A l'appel de la fonction par l'instruction table(8), la fonction table() est exécutée avec la valeur 8 pour le paramètre n. Une chaîne de caractères correspondant à la table du 8 est donc calculée par la machine. Mais comme nous demandons seulement que cette chaîne soit calculée et pas qu'elle soit affichée, on ne voit rien à l'écran.

    Si l'on veut afficher la table :

    
    def table(n) :
    	""" 
    	Fonction permettant de retourner la table de multiplication du nombre n qui est en paramètre
    	"""
    	ch='' # initialisation de ch à la chaîne vide 
    	for k in range(1,11) :
    			ch+='{}*{} = {}'.format(n,k,n*k)+'\n'
    	return ch  # ch sera l'image de n 
    	
    
    table_du_8=table(8)   # appel à la fonction 
                          # et affectation
                          #  de l'image de 8 par cette fonction 
                          # à la variable table_du_8. 
    print(table_du_8) 	 # table_du_8 
    					  # est une chaîne  de caractères,
    					  # on demande son affichage.
    

    A propos de la remarque qui vient d'être faite (Dès que la fonction rencontre un return, elle retourne ce qui suit le return et stoppe son exécution.)

    Le rôle de découpe en petits programmes

    Il nous est arrivé d’écrire des programmes contenant des instructions qui reviennent plusieurs fois parce qu’il fallait faire plusieurs fois la même chose. Voici un exemple de programme, présentant des répétitions, qui affiche les horaires du train partant de châlons en champagne :

    
    print("Le train à destination de ",  end=" ") 
    print("Paris ", end=" ")
    print(" partira à ", end=" ") 
    print("10h16 ", end="")
    print(" et arrivera à ", end=" ")
    print(" 11h53 ", end=" ") 
    print()
    print("-----------------------------------------------") 
    print()
    print("Le train à destination de ", end=" ") 
    print("Reims ", end=" ")
    print(" partira à ", end=" ") 
    print("8h41", end=" ") 
    print(" et arrivera à ", end=" ")
    print(" 9h23", end=" ")
    print()
    print("-----------------------------------------------") 
    print()
    print("Le train à destination de ", end=" ")  
    print("Saint Dizier ", end=" ")
    print(" partira à ", end=" ") 
    print("10h35 ", end=" ") 
    print(" et arrivera à ", end=" ") 
    print(" 10h54 ", end=" ") 
    print()
    print("-----------------------------------------------")
    print()
    print("Le train à destination de ", end=" ") 
    print("Epernay ", end=" ")
    print(" partira à ", end=" ") 
    print("8h15", end=" ") 
    print(" et arrivera à ", end=" ")
    print(" 8h31", end=" ")
    print()
    print("-----------------------------------------------") 
    print()   
    

    ce qui affiche

    Le train à destination de  Paris   partira à  10h16  et arrivera à   11h53  
    -----------------------------------------------
    
    Le train à destination de  Reims   partira à  8h41  et arrivera à   9h23 
    -----------------------------------------------
    
    Le train à destination de  Saint Dizier   partira à  10h35   et arrivera à   10h54  
    -----------------------------------------------
    
    Le train à destination de  Epernay   partira à  8h15  et arrivera à   8h31 
    -----------------------------------------------

    Un rôle important des fonctions est de découper votre programme en "petites unités logiques" faciles à comprendre. L'usage des fonctions, par ce rôle, facilite la conception d'un programme mais aussi sa relecture, sa réutilisation, les corrections.

    On va chercher à réécrire ce programme.

    Pour rendre ce programme plus facile à comprendre, on va le découper en fonctions, chacune des fonctions écrites devra avoir un rôle spécifique dans l'objectif de la résolution de ce problème.

    Première fonction :

    Notre première fonction devra sauter une ligne, ensuite tirer un trait séparateur puis de nouveau sauter une ligne.

    
    def tirerUnTrait () :
        """
        Une fonction qui permet de tirer un trait séparateur
        """
        print()
        print("---------------------------------------------------------------") 
        print()  
    	
    ## La suite est une utilisation de la fonction ##
    tirerUnTrait ()
    

    On obtient :

    
    ---------------------------------------------------------------
    
    

    Seconde fonction :

    Notre second programme aura pour objectif d'afficher la destination d'un train, l'horaire de départ et l'horaire d'arrivée d'un train.

    
    def annoncerUnDepart(destination, horaireDepart, horaireArrivee) :
        """
        Une fonction qui permet d'afficher la destination, l'horaire
        de départ et l'horaire d'arrivée d'un train
        """
        print("Le vol en direction de ", end = " ")
        print(destination, end = " ")
        print(" partira à ", end = " ") 
        print(horaireDepart, end = " ")
        print(" et arrivera à ", end = " ") 
        print(horaireArrivee, end = " ")
    

    Ou bien

    
    def annoncerUnDepart(destination, horaireDepart, horaireArrivee) :
        """
        Une fonction qui permet d'afficher la destination, l'horaire
        de départ et l'horaire d'arrivée d'un train
        """
        print("Le vol en direction de {} partira à {} et arrivera à {}\
        ".format(destination,horaireDepart,horaireArrivee))
    

    Remarque

    1. Dans une fonction, on peut faire appel à une autre fonction. Elles doivent être dans un même fichier (il existe toutefois des techniques, avec import, permettant d'écrire ces fonctions dans des fichiers séparés.) par exemple avec from Nomfichier import * où Nomfichier est le fichier avec l'extension py contenant toutes les fonctions qu'on utilise dans un programme.
    2. On commence à voir ici l'intérêt du découpage en fonctions : si l'on accepte que la première fonction fait bien ce que son docstring affirme, cette deuxième fonction est facile à écrire, facile à comprendre. Les fonctions permettent ainsi de "découper" les difficultés en plus petits problèmes plus faciles à résoudre.
    3. Ce découpage, nous l'avons signalé, facilite la maintenance d'un programme. Si nous nous apercevons par exemple que notre première fonction contient un bug, nous pouvons la réécrire, la corriger. Mais si nous respectons la nature des entrées et des sorties, cette réécriture de la première fonction n'entraînera aucune modification de la seconde fonction. Vous pouvez facilement imaginer l'intérêt de cela sur un programme comportant plusieurs dizaines de fonctions.

    Vous pourrez par exemple vérifier que si l'on modifie la première fonction de l'une des façons ci-dessous, la seconde fonction fonctionnera en apparence exactement de la même façon.

    Si on nomme fonctions.py le fichier en python contenant les deux fonctions écrites ci-dessus, voici un programme utilisant ces fonctions

    
    from fonctions import *	
    #Programme principal
    
    ## utilisation de la fonction ##               
    annoncerUnDepart ("Reims","8h41", "9h23")
    tirerUnTrait ()
    annoncerUnDepart ("Paris","10h16", "11h53")
    tirerUnTrait ()
    annoncerUnDepart ("Saint-Dizier","10h35", "10h54")
    tirerUnTrait ()
    annoncerUnDepart ("Epernay","08h15", "08h31") 
    

    Affichage :

    Le train à destination de  Paris   partira à  10h16  et arrivera à   11h53  
    -----------------------------------------------
    
    Le train à destination de  Reims   partira à  8h41  et arrivera à   9h23 
    -----------------------------------------------
    
    Le train à destination de  Saint Dizier   partira à  10h35   et arrivera à   10h54  
    -----------------------------------------------
    
    Le train à destination de  Epernay   partira à  8h15  et arrivera à   8h31 
    -----------------------------------------------

    Dans ce cours on traîte un exemple plus approfondi avec plusieurs fonctions.

    Fonction Lambda

    Python nous permet de définir des mini-fonctions d’une ligne. Ces fonctions dites lambda peuvent être employées partout où une fonction est nécessaire.

    Exemple

    >>>def f(x) :
    ...     return x**2
    ... 
    >>> f(3)
    9
    >>> 
    

    Voici une fonction lambda qui fait la même chose que la fonction ordinaire précédente. On remarque que la syntaxe est condensée, il n’y a pas de parenthèses autour de la liste d’arguments et le mot-clé return est manquant (il est implicite, la fonction complète ne pouvant être qu’une seule expression)

    cette fonction n'a pas de nom, mais on peut l'affecter à une variable pour l'appeler à travers cette variable.

    >>>g = lambda x : x**2
    >>> g(3)
    9 
    >>>