6. Les fonctions - premier contact#

Les fonctions sont des éléments fondamentaux en programmation. Elles permettent de structurer le code, de le rendre plus lisible et plus simple à maintenir ou à faire évoluer. Nous en avons déjà manipuler plusieurs : print, input, int, float et str. Nous introduisons dans ce chapitre les éléments principaux et nous reviendrons dessus ultérieurement.

6.1. Déclaration et appel d’une fonction#

Voici la définition d’une fonction simple (lignes 1 et 2) qui calcule le carré d’un nombre, suivit de son utilisation (ligne 4).

def square(x):
    return x*x

s = square(10)
print(s)
100

Le mot-clef def indique nous allons définir une fonction. Il est suivit de son nom et, entre paranthèse, de paramètres. Les deux points, :, marquent le début du bloc de code de la fonction. Ce sont les instructions qui seront exécutées chaque fois que l’on appelera la fonction. Chaque ligne du bloc doit être indentée c’est-à-dire décalée d’une tabulation vers la droite. L’indentation est sémentique, elle signifie que l’instruction appartient au bloc de code de la fonction.

Attention, l’indentation est souvent source d’erreur chez les programmeurs novices. L’omission des deux points est également une erreur fréquente (SyntaxError: expected ':').

Pour appeler une fonction, on utiliser son nom suivit de paranthèse avec les valeurs que l’on souhaite donner aux attributs. Ainsi, ligne 4, square(10) appel la fonction square et lie la variable x de la fonction avec la valeur 10. Le bloc de code de la fonction est alors exécuté. Ici il ne contient qu’une seule instruction return x*x. Celle-ci indique la valeur qui sera substituée à square(10). On obtiendra donc l’affichage de x*x, soit 100 dans notre cas.

6.2. Portés des variables#

La portés d’une variable correspond la zone du programme dans laquelle celle-ci est accessible et manipulable. Les paramètres d’une fonction f ainsi que toute variable qui serait déclarée dans f ne sont accessibles que dans f.

Ainsi l’exemple suivant génère une erreur NameError car la variable y n’est accessible que dans le bloc de code de la fonction cube. Nous reviendrons ultérieurement sur la notion de porté ainsi que l’espace de nom.

def cube(x):
    y = x*x*x
    return y

cube(10)
print(y)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[2], line 6
      3     return y
      5 cube(10)
----> 6 print(y)

NameError: name 'y' is not defined

6.3. Paramètres des fonctions et appel#

La fonction que nous avons vu précédement, square, prend en paramètre une valeur. Il est possible de définir des fonctions prenant plus de paramètres ou aucun. Dans tous les cas, lors de l’appel d’une fonction, il est nécessaire de spécifier entre paranthèses le nombre exact des paramètres.

def discriminant(a, b, c):
    return b*b-4*a*c

print( discriminant(1, 2, 1) )

Les paranthèses sont nécessaire pour l’appel d’une fonction même pour une fonction ne prenant pas de paramètre. Par exemple, la fonction input, que nous avons déjà vu, permet de récuperer une chaine de caractères saisie au clavier. Pour l’utiliser, on doit utiliser les paranthèse : input().

Voici ce qui se passe, si l’on utilise pas les paranthèses. Dans l’exemple suivant, nous avons volontairement omis les paranthèses. Cela ne produit pas d’erreur mais au lieu d’exécuter la fonction input, on la manipule comme une valeur. C’est possible en Python car les fonctions sont des valeurs au même titre que les entiers ou les chaines de caractères.

x = input
print(x)

Une fonction étant une valeur comme une autre, la variable x est maintenant lié à la fonction qui permet de saisir une chaine de caractères au clavier. On peut donc saisir une chaine en utilisant x()

Si vous comprenez bien ce qui se passe, alors c’est parfait. Si cela vous parrait malaisant alors ça devrait vous convaincre qu’il faut être vigilent et bien utiliser les paranthèses. Pour le moment nous n’aurons pas besoin de manipuler les fonctions autrement.

x = input
print(x())

6.4. Fonctions avec et sans résultat#

Nous avons déjà vu le mot clef return. Ce dernier permet d’indiquer ce que vaut une fonction à la fin de son exécution. La valeur qui suit return est substituée à l’appel de cette fonction. Elle peut donc être utiliser dans un calcul comme on le voit à la ligne 5 de l’exemple suivant.

Le mot clef return est la dernière instruction exécutée dans une fonction. Une fois la valeur de retour calculé, Python quitte la fonction. L’instruction de la ligne 3 n’est donc jamais exécutée.

def square(x):
    return x*x
    print("un message jamais affiché")

c = square(10)*10
print(c)

Python permet de définir des fonctions sans utiliser le mot clef return. Ces fonctions auront tout de même une valeur de retour par défaut : None.

Nous en avons déjà rencontré de telles fonctions, par exemple la fonction print. Cette dernière affiche une valeur à l’écran mais n’a pas de valeur. Testons l’exemple suivant.

a = print("X")
print(a)

Que se passe-t-il ? Nous avons dit que la fonction print n’avait pas de valeur de retour. Sa valeur est donc None. Donc à l’exécution de print("X"), il sera afficher à l’écran X puis la valeur None viendra se substituer à print("X") et sera affecté à a. C’est pour cela que None est ensuite affiché.

Si vous avez compris cet exemple vous devriez également comprendre celui ci.

print(print("X"))

6.5. Quelques clarifications#

Voici des éléments supplémentaires et des clarifications sur le comportement des fonctions.

<video src="../_static/videos/03_fonctions.mp4" style="width:100%" type="video/mp4" controls></video>

6.6. Exercices sur les fonctions#

Pour ces exercices, il va devenir un peu pénible d’utiliser l’interpréteur Python. Nous allons donc utiliser un fichier Python pour chaque questions. je vous conseille de stocker tous ces fichiers dans un répertoire.

Question : Créez une fonction one qui ne prend pas de paramètre et renvoie la valeur 1. Utilisez cette fonction dans un calcul.

Utilisation des mots clefs def et return.

def one():
    return 1

1 + one()

Note

Attention, quand vous utilisez votre fonction à bien mettre les paranthèses sinon la fonction n’est pas exécutée. Ici on utilise l’erreur unsupported operand type(s) for +: 'int' and 'function' qui indique qu’on ne peut pas additionner une fonction et un entier.

1 + one


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In [7], line 1
----> 1 1 + one

TypeError: unsupported operand type(s) for +: 'int' and 'function'

Question : Créez une fonction plus_one qui prend un paramètre x et renvoie la valeur x+1. Utilisez la fonction one dans le corps de votre fonction. Testez votre fonction.

On peut tout à fait appeler les fonctions les unes dans les autres.

def plus_one(x):
    return x+one()

plus_one(10)
11

Question : Créez une fonction mean qui prend deux paramètres a et b. La fonction renvoie la valeur (a+b)/2. Quel est le type de la valeur de retour de cette fonction ? Testez votre fonction.

On peut utiliser des variables dans une fonction, elles sont alors locales. Le type de la valeur de retour dépend des oppérations contenues dans la fonction.

def mean(a, b):
    tmp = a+b
    return tmp/2

divide(0, 20)
10.0

Question : Sans l’exécuter, pouvez vous déterminer la valeur renvoyée par l’exécution de l’instruction suivante :

mean(mean(0, 10), plus_one(one()))

Cette instruction est exécutée en commençant par les paranthèses les plus internes. Voici donc ce qui se passe à l’exécution. La fonction one est exécutée et la valeur de retour ne la fonction vient se substituer à son appel. On obtient donc

mean(mean(0, 10), plus_one(1))

À la suite de cela, c’est la fonction plus_one qui doit être exécutée. On obtient donc

mean(mean(0, 10), 2)

C’est maintenant au tour de la fonction mean la plus interne. On obtient

mean(5.0, 2)

ce qui vaut 3.5.