Skip to content

Référence vers une fonction

Énoncé

Dans cet exercice nous allons voir qu'il est possible de manipuler des fonctions de la même manière que l'on manipule des instances de classes. Autrement dit, il est possible d'avoir des références vers des fonctions, et de passer des fonctions en paramètres d'une fonction.

Comme une image vaut mille mots, interrogeons-nous sur le code ci-dessous en nous posant les questions suivantes :

  • est-il correct, c'est-à-dire exécutable et ne donnant lieu à aucune erreur ?
  • qu'affiche-t-il ?
  • quel est l'état du programme une fois que les variables entier, fonc1 et fonc2 ont été définies ?
 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
#!/usr/bin/env python3
"""Un exemple de références vers des fonctions"""


def multiplie_par_deux(un_nombre):
    """Renvoie un_nombre multiplié par deux"""
    return un_nombre * 2


def ajoute_42(un_nombre):
    """Renvoie un_nombre + 42"""
    return un_nombre + 42


def applique_operation(un_nombre, operation):
    return operation(un_nombre)


def main():
    """Test de nos fonctions"""
    entier = 17
    fonc1 = multiplie_par_deux
    fonc2 = ajoute_42
    print(multiplie_par_deux(entier))
    print(fonc1(entier))
    print(applique_operation(entier, multiplie_par_deux))
    print(applique_operation(entier, fonc2))
    print(applique_operation(entier, fonc1(entier)))


if __name__ == "__main__":
    main()

Correction

Cliquez ici pour révéler la correction.

La sortie du programme est la suivante :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
34
34
34
59
Traceback (most recent call last):
  File "./fonction.py", line 59, in <module>
    main()
  File "./fonction.py", line 56, in main
    print(applique_operation(entier, fonc1(entier)))
  File "./fonction.py", line 17, in applique_operation
    return operation(un_nombre)
TypeError: 'int' object is not callable

Il n'est donc pas entièrement correct comme l'indique le message d'erreur. Le deuxième argument de la fonction applique_operation doit être une fonction, or dans le troisième appel à cette fonction on lui passe un entier.

Voici l'état de la mémoire une fois que toutes nos variables sont définies.

mémoire

Et voici le code de correction de fonction.py :

 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
#!/usr/bin/env python3
"""Un exemple de références vers des fonctions"""
import traceur



def multiplie_par_deux(un_nombre):
    """Renvoie un_nombre multiplié par deux"""
    return un_nombre * 2


def ajoute_42(un_nombre):
    """Renvoie un_nombre + 42"""
    return un_nombre + 42


def applique_operation(un_nombre, operation):
    """Renvoie le résultat de l'opération donnée sur le nombre donné"""
    return operation(un_nombre)


def main():
    """Test de nos fonctions"""
    entier = 17
    # fonc1 et fonc2 sont des références vers une fonction
    fonc1 = multiplie_par_deux
    fonc2 = ajoute_42
    variables = [
        traceur.Variable("entier", entier),
        traceur.Variable("fonc1", fonc1),
        traceur.Variable("fonc2", fonc2),
        traceur.Variable("multiplie_par_deux", multiplie_par_deux),
        traceur.Variable("ajoute_42", ajoute_42),
        traceur.Variable("applique_operation", applique_operation),
    ]
    traceur.display_vars(*variables)
    # On appelle multiplie_par_deux comme d'habitude.
    # C'est à dire en utilisant son nom.
    # Ce-dernier n'est en fait qu'une référence vers
    # la fonction elle-même.
    print(multiplie_par_deux(entier))
    # fonc1 est une référence vers une fonction
    # au même titre que multiplie_par_deux.
    # On peut donc l'appeler.
    print(fonc1(entier))
    # On passe une référence vers une fonction
    # en paramètre à la fonction applique_operation
    print(applique_operation(entier, multiplie_par_deux))
    # On passe une référence vers une fonction
    # en paramètre à la fonction applique_operation
    print(applique_operation(entier, fonc2))
    # On passe une référence vers une fonction
    # en paramètre un entier à la place d'une fonction
    # à la fonction applique_operation --> ERREUR
    print(applique_operation(entier, fonc1(entier)))


if __name__ == "__main__":
    main()