fonctions python

Identification

Infoforall

8 - Fonctions et variables


Nous allons voir un aspect fondamental de la programmation : la possibilité d'insérer des "mini-programmes" dans les programmes de façon à créer de nouvelles actions. Vous avez déjà utilisé de nombreuses fois les fonctions. Lesquelles ?

  • print() est une fonction (native) qui permet d'afficher des choses dans la console,
  • int() est une fonction (native) qui permet de créer un nouveau contenu de type integer, basé sur ce qu'on lui envoie
  • type() est une fonction (native) qui renvoie le type de la donnée fournie en entrée.

Il s'agit donc de sortes de boites noires à qui on fournit des ENTREES et que nous répondent en retour.

ENTREE(S)  ⇒   Fonction   ⇒  SORTIE

Vous savez actuellement faire appel à ces fonctions. Nous allons voir aujourd'hui comment en créer vos propres fonctions, qui ne seront donc pas des fonctions natives, présentes de base dans Python.

Commençons par vous montrer ce qu'on peut faire assez facilement avec les fonctions (et qui peut s'avérer impossible ou presque sans elles):

animation d'introduction

Téléchargez le code à l'aide de l'image et lancez le. Nous obtenons une animation.

Sans fonction, difficile de réaliser ceci...

Logiciel nécessaire pour l'activité : Python 3

Evaluation ✎ : question 07-08-10-13-14-18-19-20

Documents de cours : open document ou pdf

Résumé : Version HTML ou fond blanc ou ou PDF (couleur ou gris)

1 - Premieres déclarations de fonctions

Commençons par revoir ce que vous savez déjà sur les fonctions.

Rappel - Savoir faire appel à une fonction

Ne zappez pas les trois rappels si vous ne les avez jamais vu. Sinon, le reste de l'activité risque de vous sembler difficile...

RAPPEL - Notion de FONCTION
ENTREE(S)  ⇒   Fonction   ⇒  SORTIE

Description

Les fonctions sont des moyens de répondre à des problèmes spécifiques.

  • on leur donne des entrées : les données sur lesquelles on veut qu'elles travaillent et
  • elles répondent en renvoyant une réponse en sortie.
ENTREE(S)  ⇒   Fonction   ⇒  SORTIE

Un exemple avec la fonction VALEUR_ABSOLUE qui n'attend qu'une entrée (un nombre) et qui renvoie la valeur absolue du nombre (le même nombre mais sans son signe).

-12  ⇒   Fonction VALEUR ABSOLUE   ⇒  12

12  ⇒   Fonction VALEUR ABSOLUE   ⇒  12

D'autres exemples

5; 10; 50; 0; 20  ⇒   Fonction MAXIMUM   ⇒  50

5; 10; 50; 0; 20  ⇒   Fonction MINIMUM   ⇒  0

5; 10; 50; 0; 20  ⇒   Fonction SOMME   ⇒  85
RAPPEL - Syntaxe Python de l'appel d'une fonction

nom_fonction(entrée_1, entrée_2, ...)

La syntaxe d'utilisation des fonctions en Python est la suivante :

nom_fonction(a, b, c, d...)

  1. on écrit le nom de la fonction
  2. on place les deux parenthèses ouverture-fermeture à la suite du nom
  3. on place les ENTREES dans les parenthèses, en les séparant par des virgules au besoin

Python intégre plusieurs fonctions natives : des fonctions présentes de base dans Python. Dans le cadre de ce site, les fonctions natives sont toujours écrites en vert.

Fonction VALEUR ABSOLUE : cette fonction se nomme abs() en Python.

>>> abs(-12) 12 >>> abs(12) 12

Fonction MAXIMUM : cette fonction se nomme max() en Python.

>>> max(10, 0, 50, 40) 50
RAPPEL - Affectation d'une variable

Affecter un contenu à une variable veut dire placer le contenu en mémoire pour pouvoir y faire appel plus loin dans le programme.

Un variable est une association entre

  • un nom de variable et
  • une donnée (type simple, comme le 50) ou des données (type construit, comme le tableau [10, 20, 30]).

La création de cette association se nomme une affectation.

>>> a = 50 >>> t = [10, 20, 30]

Pour faire appel au contenu d'une variable, il suffit de taper son nom puisque c'est juste le contenu qui importe.

>>> a 50 >>> t [10, 20, 30]

Déclaration de vos propres fonctions

1 - VOCABULAIRE : Déclaration d'une fonction

Déclarer une fonction veut dire la placer en mémoire pour pouvoir y faire appel plus loin dans le programme.

Une fonction va être plus complexe à déclarer car c'est une association entre

  • un nom de fonction, et
  • une ou des données d'entrée à stocker (pour savoir sur quoi travailler), et
  • des instructions à stocker (pour savoir quoi faire des données)
  • la réponse de sortie à fournir au final (une fois que les instructions auront traité les données)

La création de cette association complexe se nomme une déclaration de fonction.

2 - Syntaxe Python d'une déclaration
1 2 3
def ma_fonction(x): resultat = x * 2 return resultat

Voici comment déclarer une fonction :

  1. On commence la déclaration par le mot-clé def suivi du nom de la fonction. Comme pour les variables, le nom de la fonction doit être explicite : son nom doit permettre de comprendre ce qu'elle réalise.
  2. 1
    def fois2
  3. on rajoute des parenthèses,
  4. 1
    def fois2()
  5. entre les parenthèses, on fournit les noms des variables qui vont servir à mémoriser les ENTREES que la fonction va recevoir dans le futur (ici, c'est x).
  6. 1
    def fois2(x)

    Vocabulaire : ces variables stockant les entrées se nomment les paramètres de la fonction.

  7. On finit cette première ligne par un POINT DOUBLE : signifiant qu'on a fini de fournir le nom et les paramètres d'entrée.
  8. 1
    def fois2(x):
  9. Les instructions à réaliser par la fonction sont toutes indentées (4 espaces ou touche TAB). C'est comme cela que l'interpréteur Python qu'une instruction appartient à la fonction (ou pas).
  10. 1 2
    def fois2(x): resultat = x * 2
  11. On finit par return suivi de la réponse qu'on veut renvoyer, ici resultat
  12. 1 2 3
    def fois2(x): resultat = x * 2 return resultat

"Déclarer la fonction" n'est pas "Faire appel à la fonction"

Il faut comprendre qu'après avoir exécuter ces lignes, l'interpréteur Python ne lance pas d'appel à votre fonction. D'ailleurs, il aurait du mal... Que mettrait-il dans le paramètre x de toutes manières ?

Déclarer la fonction permet juste de mettre ce nom en mémoire pour pouvoir y faire appel plus loin dans le programme.

01 ✔° Réaliser les actions suivantes 

  1. Ouvrir l'onglet VARIABLES de Thonny pour vérifier que la mémoire est vide initialement
  2. Sauvegarder et lancer le programme suivant sous le nom activite_fonctions.
  3. Vérifier via l'onglet VARIABLES que la mémoire contient maintenant deux nouvelles variables nommées fois2 et plus2.
1 2 3 4 5 6 7
def fois2(x): resultat = x * 2 return resultat def plus2(x): resultat = x + 2 return resultat

Vous devriez avoir ceci à l'écran :

résultat dans thonny

02° Les fonctions sont maintenant en mémoire. Vous allez maintenant pouvoir faire appel à vos fonctions. Réaliser les appels suivants dans la console de Python, observer les réponses et puis répondre aux questions :

>>> plus2(10) ???
  • A quelle fonction fait-on appel ?
  • Lors d'un appel à une fonction, on doit-on placer la valeur d'entrée qu'on veut envoyer à notre fonction ?
  • Quelle est la valeur envoyée à la fonction lors de cet appel ?
  • Quel est le contenu affecté au paramètre x lors de cet appel ?
  • Que va contenir resulat lors de cet appel ?
  • Quelle est la réponse renvoyée par cet appel de fonction (qui s'affiche dans la console, puisqu'on en fait rien) ?
>>> plus2(15) ???
  • A quelle fonction fait-on appel ?
  • Quelle est la valeur envoyée à la fonction lors de cet appel ?
  • Quel est le contenu affecté au paramètre x lors de cet appel ?
  • Que va contenir resulat lors de cet appel ?
  • Quelle est la réponse renvoyée par cet appel de fonction (qui s'affiche dans la console, puisqu'on en fait rien) ?
>>> fois2(10) ???
  • A quelle fonction fait-on appel ?
  • Quelle est la valeur envoyée à la fonction lors de cet appel ?
  • Quel est le contenu affecté au paramètre x lors de cet appel ?
  • Que va contenir resulat lors de cet appel ?
  • Quelle est la réponse renvoyée par cet appel de fonction (qui s'affiche dans la console, puisqu'on en fait rien) ?

...CORRECTION...

>>> plus2(10) 12
  • A quelle fonction fait-on appel ?
  • A la fonction nommée plus2.

  • Lors d'un appel à une fonction, on doit-on placer la valeur d'entrée qu'on veut envoyer à notre fonction ?
  • La SYNTAXE Python impose de placer l'entrée entre les parenthèses.

  • >Quelle est la valeur envoyée à la fonction lors de cet appel ?
  • Ici, on voit que l'entrée est 10.

  • Quel est le contenu affecté au paramètre x lors de cet appel ?
  • Le paramètre x référence l'entier 10.

  • Que va contenir resulat lors de cet appel ?
  • La variable resultat référencera 12 après exécution de la ligne 6.

  • Quelle est la réponse renvoyée par la fonction (qui s'affiche dans la console, puisqu'on en fait rien) ?
  • Cet appel renvoie 6.

>>> plus2(15) 17
  • A quelle fonction fait-on appel ?
  • Toujours à plus2.

  • Quelle est la valeur envoyée à la fonction lors de cet appel ?
  • Cette fois, on envoie 17.

  • Quel est le contenu affecté au paramètre x lors de cet appel ?
  • x référence 15.

  • Que va contenir resulat lors de cet appel ?
  • resulat référence donc 17.

  • Quelle est la réponse renvoyée par la fonction (qui s'affiche dans la console, puisqu'on en fait rien) ?
  • Cet appel renvoie 17.

>>> fois2(10) 20
  • A quelle fonction fait-on appel ?
  • Cette fois, la fonction appelée est fois2.

  • Quelle est la valeur envoyée à la fonction lors de cet appel ?
  • On envoie une entrée valant 10.

  • Quel est le contenu affecté au paramètre x lors de cet appel ?
  • x référence 10.

  • Que va contenir resulat lors de cet appel ?
  • resultat référence alors 20 (voir ligne 2).

  • Quelle est la réponse renvoyée par la fonction (qui s'affiche dans la console, puisqu'on en fait rien) ?
  • Cet appel renvoie donc 20.

3 - Déclaration et appel, paramètre et argument
1 2 3
def valeur(d, u): resultat = d*10 + u return resultat
>>> valeur(5, 3) 53

L'utilisation d'une fonction se fait en deux temps :

  1. D'abord la déclaration : c'est la phase précédente. On donne toute les informations pour l'utiliser et on le place en mémoire. On obtient ainsi une sorte de variable contenant toutes ces informations.
  2. Ensuite, on peut en faire des appels : on l'utilise réellement en lui envoyant des entrées.

Pour comprendre comment la fonction fonctionne lors d'un appel, il faut justaposer

  • son appel qui permet de savoir quelles sont les entrées envoyées à la fonction sur cet appel. Plutôt que de dire les "entrées envoyées à la fonction sur cet appel", on utilisera le mot argument.
  • 1a 1er ligne de la déclaration qui contient les paramètres de stockage des entrées.

L'argument n°1 va dans le paramètre n°1 ect...

Cas d'une fonction avec 1 paramètre

1
def fois2(x): <-- x est le paramètre de déclaration
>>> fois2(20) <-- 20 est l'argument de l'appel 40

Lors de l'appel, on envoie l'argument 20.

L'argument 20 est alors stocké dans le paramètre x

Cas d'une fonction avec 2 paramètres

1
def valeur(d, u): <-- d et u sont les paramètres
>>> valeur(5, 3) <-- 5 et 3 sont les arguments de l'appel

Lors de l'appel, on envoie l'argument 5 suivi de l'argument 3.

L'argument 5 est donc stocké dans d et l'argument 3 dans le paramètre u

Moyen mnémotechnique pour les interros

appel (commence par a) : argument (comme par a aussi)

déclaration (commence par d) : paramètre (comme par p, un d à l'envers)

03° Réaliser les appels suivants dans la console de Python, observer les réponses et puis répondre aux questions :

>>> a = plus2(100)
  • A quelle fonction fait-on appel ?
  • Quel est l'argument envoyé à la fonction lors de cet appel ?
  • Quel est le contenu affecté au paramètre x lors de cet appel ?
  • Que va contenir resulat lors de cet appel ?
  • Quelle est la réponse renvoyée par cet appel de fonction ?
  • Pourquoi n'obtient-on pas d'affichage dans la console ?
>>> b = fois2(a)
  • A quelle fonction fait-on appel ?
  • Quel est l'argument envoyé à la fonction lors de cet appel ?
  • Quel est le contenu affecté au paramètre x lors de cet appel ?
  • Que va contenir resulat lors de cet appel ?
  • Quelle est la réponse renvoyée par cet appel de fonction ?
  • Pourquoi n'obtient-on pas d'affichage dans la console ?
>>> c = fois2(plus2(10))
  • Que va évaluer d'abord l'interpréteur Python à cause des priorités ?
  • Quel est l'argument envoyé à la fonction fois2() lors de cet appel ?
  • Expliquer le contenu de c.

...CORRECTION...

>>> a = plus2(100)
  • A quelle fonction fait-on appel ?
  • On utilise plus2()

  • Quel est l'argument envoyé à la fonction lors de cet appel ?
  • L'argument envoyé est 100.

  • Quel est le contenu affecté au paramètre x lors de cet appel ?
  • x contient donc 100.

  • Que va contenir resulat lors de cet appel ?
  • resultat contient donc 102 car on calcule 100 + 2.

  • Quelle est la réponse renvoyée par cet appel de fonction ?
  • 102 qu'on affecte à la variable a.

  • Pourquoi n'obtient-on pas d'affichage dans la console ?
  • La console n'affiche une évaluation que si c'est la seule chose sur la ligne. Ici, il y a une affectation dans une variable.

>>> b = fois2(a)
  • A quelle fonction fait-on appel ?
  • On utilise fois2()

  • Quel est l'argument envoyé à la fonction lors de cet appel ?
  • On envoie a, soit 102.

  • Quel est le contenu affecté au paramètre x lors de cet appel ?
  • x fait donc référence à 102 lors de cet appel.

  • Que va contenir resulat lors de cet appel ?
  • Le double de 102, soit 204.

  • Quelle est la réponse renvoyée par cet appel de fonction ?
  • La fonction renvoie 204 et on stocke le résultat dans la variable b.

  • Pourquoi n'obtient-on pas d'affichage dans la console ?
  • Comme au dessus : le résultat de l'évaluation possède déjà une destination, la variable.

>>> c = fois2(plus2(10))
  • Que va évaluer d'abord l'interpréteur Python à cause des priorités ?
  • En respectant la priorité des parenthèses, on se rend comppte qu'il faut d'abord évaluer plus2(10).

  • Quel est l'argument envoyé à la fonction fois2() lors de cet appel ?
  • Python va évaluer plus2(10) à la valeur 12.

    On envoie donc ce 12 comme argument à fois2(). L'argument 12 est donc affecté au paramètre x.

  • Expliquer le contenu de c.
  • c récupère donc la valeur évaluée par fois2(12), soit 24.

04° Réaliser les appels suivants qui provoquent tous des erreurs. Pour chaque appel, observer la dernière ligne du message d'erreur de Python et répondre aux questions proposées :

>>> fois2() ???
  • Quel est le type d'erreur ?
  • A quoi est due cette erreur ?
>>> fois2(10, 20) ???
  • Quel est le type d'erreur ?
  • A quoi est due cette erreur ?
>>> fois2 10 ???
  • Quel est le type d'erreur ?
  • A quoi est due cette erreur ?
>>> fois2([10]) ???
  • Quel est le type d'erreur ?
  • A quoi est due cette erreur ?

...CORRECTION...

>>> fois2() TypeError: fois2() missing 1 required positional argument: 'x'
  • Quel est le type d'erreur ?
  • C'est une erreur TypeError. En Python, il s'agit d'une erreur d'exécution.

  • A quoi est due cette erreur ?
  • On envoie 0 argument lors de l'appel alors que la déclaration de la fonction possède UN paramètre.

>>> fois2(10, 20) TypeError: fois2() takes 1 positional argument but 2 were given
  • Quel est le type d'erreur ?
  • C'est une erreur TypeError. En Python, il s'agit d'une erreur d'exécution.

  • A quoi est due cette erreur ?
  • On envoie 2 arguments lors de l'appel alors que la fonction possède UN paramètre.

>>> fois2 10 SyntaxError: invalid syntax
  • Quel est le type d'erreur ?
  • C'est une erreur de SYNTAXE. Python ne comprend pas l'appel.

  • A quoi est due cette erreur ?
  • L'utilisateur a oublié les parenthèses en faisant l'appel.

>>> fois2([10]) [10, 10]
  • Quel est le type d'erreur ?
  • Par d'erreur en soi : pas d'erreur de syntaxe, ni d'erreur d'exécution.

    Par contre, si l'utilisateur voulait vraiment obtenir un entier multiplié par deux, c'est une erreur de sémantique : il envoie un tableau de une case. Python fait son travail, il renvoie un tableau de deux cases identiques.

  • A quoi est due cette erreur ?
  • A l'opérateur * qui possède une signature int * list -> list dont la sémantique est la répétition et pas la multiplication au sens mathématique.

Erreur à éviter pendant la séance

C'est l'indentation vers la droite qui permet à Python de comprendre quelles instructions appartiennent à la fonction, ou pas. Il faut donc placer une tabulation ou 4 espaces de suite pour savoir qu’elles sont les instructions rattachées au bloc.

Les lignes 2 et 3 appartiennent à la fonction déclarée ligne 1 puisqu'elles sont décalées de 4 espaces vers la droite.

1 2 3 4 5 6 7
def fois2(x): ....resultat = x * 2 ....return resultat def plus2(x): ....resultat = x + 2 ....return resultat

05° Aller sur Python Tutor, et visualiser le déroulé du programme suivant en mode pas à pas.

1 2 3 4 5 6 7
def valeur(d, u): resultat = d*10 + u return resultat a = valeur(2, 3) b = valeur(5, 7) c = valeur(9, 0)

Questions une fois avoir vu le déroulé par à pas

  • Lorsqu'on exécute le bloc de déclaration, met-on la fonction en mémoire ou lance-t-on la fonction ?
  • Chaque appel de fonction dispose-t-il de son propre bloc de mémoire ou place-t-on les arguments dans l'espace mémoire créé en ligne 1 ?
  • Que devient un bloc-mémoire de fonction après que la fonction ai répondu ?

...CORRECTION...

  • Lorsqu'on exécute le bloc de déclaration, met-on la fonction en mémoire ou lance-t-on la fonction ?
  • On place juste la fonction en mémoire. C'est une déclaration, pas un appel.

  • Chaque appel de fonction dispose-t-il de son propre bloc de mémoire ou place-t-on les arguments dans l'espace mémoire créé en ligne 1 ?
  • Chaque appel de fonction génére son propre espace mémoire lui permettant de stocker les valeurs d'arguments reçus.

  • Que devient un bloc-mémoire de fonction après que la fonction ai répondu ?
  • Dès qu'une fonction a répondu, le bloc-mémoire de cet appel est supprimé puisqu'il ne sert plus à rien.

06° Placer la fonction valeur() en mémoire dans Thonny. Utiliser ensuite ceci dans la console :

1 2 3
def valeur(d, u): resultat = d*10 + u return resultat
>>> valeur(2, 6) 26

Questions

  1. Expliquer la valeur obtenue sur cet appel.
  2. La commande passée en console est-elle une simple évaluation (sans mémorisation du résultat) ou affectation (avec mémorisation du résulat) ?
  3. Que faudrait-il faire pour garder le résultat en mémoire ?

...CORRECTION...

1
def valeur(d, u):
>>> valeur(2, 6)
  1. Expliquer la valeur obtenue sur cet appel.
  2. L'argument 2 est stockée dans le paramètre d.

    L'argument 6 est stockée dans le paramètre u.

    La fonction calcule donc 2*10 + 6 et renvoie donc 26.

  3. La commande passée en console est-elle une simple évaluation (sans mémorisation du résultat) ou affectation (avec mémorisation du résulat) ?
  4. Il s'agit d'une simple évaluation de la réponse de la fonction. C'est pour cela que le résultat s'affiche.

  5. Que faudrait-il faire pour garder le résultat en mémoire ?
  6. Le stocker dans une variable. Et du coup, il ne s'affichera plus dans la console automatiquement.

    >>> m = valeur(2, 6)

✎ 07° Expliquer la réponse qu'on va obtenir lors de l'appel de la fonction facile() ci-dessous. On vous donne la déclaration de la fonction et son utilisation dans la console interactive.

1 2
def facile(x): return x*10 + 5
>>> facile(3) ???

✎ 08° Expliquer la réponse qu'on va obtenir lors de l'appel de la fonction plus_difficile() ci-dessous.

1 2
def plus_difficile(x, y, z): return x*100 + y*10 + z
>>> plus_difficile(5, 6, 3) ???

...CORRECTION...

Il faut comparer l'appel et la déclaration.

>>> difficile(5, 6)
5
def difficile(x, y):

On voit que le paramètre x est affecté à 5 et que le paramètre y est affecté à 6.

Le code de la fonction permet de voir qu'on va renvoyer 5*10+6, soit 56.

4 - Organisation d'un programme : importations-constantes-fonctions puis les instructions

Avant d'utiliser une fonction, il faut d'abord la déclarer pour la placer en mémoire. C'est pour cela que nous avions vu que les déclarations de fonctions doivent être placés avant les instructions du programme principal lui-même.

  1. Importation des modules : cette partie est à placer en premier car on importe des fonctions et des objets issus d'autres programmes.
  2. Déclaration des CONSTANTES : comme cela, n'importe quelle instruction située en dessous pourra les utiliser puisque les CONSTANTES seront déjà en mémoire.
  3. Déclaration des fonctions : on pourra donc utiliser les importations et les CONSTANTES pour réaliser nos fonctions.
  4. Instructions du programme principal : on pourra donc utiliser les fonctions natives (présentes de base), les fonctions importées (disponibles dès la partie 1), les CONSTANTES (disponibles dès la partie 2) et les fonctions déclarées (disponibles dès la partie 3).

09° Pour valider cette première partie :

  1. Appuyer sur le bouton VISUALISER ci-dessous, pour observer l'ordre d'exécution de ce programme
  2. expliquez les valeurs finales stockés dans les variables a et b après exécution du code ci-dessous.
  3. Vérifiez ensuite en lançant le code réellement.
  4. Modifier vos réponses au besoin !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# 3 - - Déclaration des fonctions - - - - def fois2(x): resultat = x * 2 return resultat def plus2(x): resultat = x + 2 return resultat # 4 - - Programme principal - - - - - nombre = 3 nombre2 = 5 a = plus2(nombre) b = fois2(a) b = b + 1

CLIQUEZ ICI POUR VOIR L'ORDRE DES INSTRUCTIONS EXECUTEES :

Pour visualiser les valeurs des deux variables, deux possibiltés :

  • Soit vous utilisez votre éditeur de code (par exemple VIEW-VARIABLES dans Thonny)
  • Soit vous demandez les résultats via la console interactive :
  • >>> a >>> b

...CORRECTION...

On obtient ceci

  • Mise en mémoire de la fonction fois2()
  • Mise en mémoire de la fonction plus2()
  • Affectation de 3 dans nombre
  • Affectation de 5 dans nombre2
  • plus2(nombre) renvoie 5.
  • Affectation de 5 dans a
  • fois2(a) renvoie 10 car a est évaluée à 5.
  • Affectation du 10 dans b
  • Evaluation de b + 1 à la valeur 11
  • Affectation du 11 dans b
Notation des fonctions

On trouve deux écoles dans la notation des fonctions lorsqu'on redige un texte en français.

Certains diront qu'il faut noter fois2() avec les parenthèses lorsqu'on parle de la fonction, comme cela on comprend que c'est une fonction.

D'autres diront qu'on doit plutôt noter juste fois2 lorsqu'on parle de la fonction car fois2() laisse entendre qu'il s'agit d'un appel de la fonction.

Le tout est de savoir que les deux possibilités existent. Il n'y a pas de normes établies à ce sujet.

Dans le cadre de ce site, c'est la notation avec parenthèses qui sera plutôt utilisée.

2 - Documentation : expliquer comment utiliser

Le but de l'informatique ?

Résoudre des problèmes en utilisant des programmes basés sur des algorithmes.

Qu'est-ce qu'un beau programme ?

  • Point 1 : d'abord des fonctions qui fonctionnent en donnant le bon résultat
  • Point 2 :ensuite des fonctions qu'on parvient à utiliser même si nous n'en sommes pas le créateur
  • Point 3 : ensuite des fonctions dont on parvient à comprendre le fonctionnement pour la modifier au besoin
  • Point 4 : enfin, des fonctions qui fonctionnent vite

Les points 1 et 4 (ça fonctionne bien et vite), c'est dans la partie Algorithmique qu'on s'en chargera.

Le point 3 veut dire qu'il vaut mieux un programme un peu plus lent mais clair qu'une suite super efficace mais incompréhensible d'instructions compliquées. Dans "langage de programmation", il y langage. Le but est de parler à la machine ET à vos collègues.

Nous avons déjà vu que pour expliquer le fonctionnement d'un programme, on peut utiliser les commentaires.

RAPPEL - Commentaires

A quoi sert un commentaire ? A permettre de modifier la fonction

Les commentaires servent à faire comprendre à un lecteur humain comment une fonction réalise le travail demandé. Une fois qu'il aura compris comment les instructions résolvent le problème, il pourra alors les modifier s'il le désire.

Si on prend l'analogie d'une voiture, les commentaires sont les explications sur le fonctionnement du moteur.

Faire des commentaires en Python ?

En Python, on rajoute des commentaires dans le code en utilisant #.

Il reste à voir le Point 2 : comment parvenir à créer des fonctions qu'on pourra utiliser même si nous n'en sommes pas le créateur.

2.1 - La documentation, un manuel d'utilisation

A quoi sert une documentation ? A permettre d'utiliser la fonction

La documentation doit expliquer comment utiliser la fonction sans provoquer d'erreur.

Si on prend l'analogie de la voiture :

  • Les commentaires expliquent comment fonctionne la voiture (cours de mécanique, d'électronique...)
  • La documentation explique comment utiliser la voiture (cours de code et de conduite)

Trouver la documentation

En Python, pour trouver la documentation, il suffit d'utiliser une fonction native : la fonction help().

Imaginons qu'on veuille récupérer la documentation de la fonction native abs() : il faut donner le nom de la fonction en tant qu'argument à la fonction type(). Attention, juste le nom, sans les parenthèses. On ne veut pas l'activer.

>>> help(abs) Help on built-in function abs in module builtins: abs(x, /) Return the absolute value of the argument.

Première ligne : on voit que abs() est une fonction native (build-in).

Help on built-in function abs in module builtins:

Deuxième ligne : à qui on doit fournir un seul argument (qui sera rangé dans un paramètre x visiblement).

abs(x, /)

Troisième ligne : on voit que la fonction va renvoyée la valeur absolue de l'argument envoyé.

Return the absolute value of the argument.

La documentation ne contient aucune information sur le fonctionnement interne de la fonction. Elle contient juste des informations sur les arguments à envoyer (la ou les ENTREES) et sur la réponse (la SORTIE).

✎ 10° Taper ceci dans la console pour récupérer la documentation (écrite par le concepteur de Python) de la fonction len().

>>> help(len) Help on built-in function len in module builtins: len(obj, /) Return the number of items in a container.

Questions

  1. len() est-elle une fonction native ?
  2. combien d'arguments doit-on envoyer à len() ?
  3. traduire la phrase expliquant ce que renvoie la fonction
2.2 Contenu basique d'une documentation : prototype+ une phrase

A - Signature d'une fonction

Pour pouvoir utiliser une fonction correctement, on a besoin :

  1. de savoir comment elle se nomme
  2. de savoir ce qu'on doit lui envoyer
  3. de savoir ce qu'elle va répondre
Arguments  ⇒   Fonction   ⇒  Réponse

Grossièrement, on a donc besoin de connaître la signature de la fonction, tout comme il est pratique de connaître la signature d'un opérateur.

Voici par exemple, l'une des signatures disponibles pour l'opérateur + :

int + int -> int

Imaginons maintenant la signature d'une fonction nommée addition() qui permettrait d'additionner deux entiers. Nous pourrions alors noter :

addition(int, int) -> int

B - Prototype d'une fonction

Prototyper une fonction consiste à rajouter les noms des paramètres à la signature.

Pour notre fonction addition(), cela pourrait donner :

addition(nombre1:int, nombre2:int) -> int

Attention : le prototype ne donne pas d'indications sur ce que réalise la fonction. On peut s'en douter en regardant les noms de la fonction et des paramètres si ils sont explicites, mais on ne fait que tenter de deviner.

C - Spécification : la documentation minimale d'une fonction

Spécifier une fonction consiste à donner le prototype et des explications permettant de donner du sens à son prototype.

  • Le prototype fixe la SYNTAXE et le TYPAGE
  • La description fixe la SEMANTIQUE

On rajoute donc une DESCRIPTION explicative :

  • qu'on place juste sous le prototype,
  • entourée de trois guillemets d'ouverture
  • et trois guillemets de fermeture.

Exemple

Voilà la description minimaliste d'une fonction : la spécification qui comprend la signature et l'explication du lien entre les arguments envoyés et la réponse obtenue.

1 2 3
def addition(nbr1:int, nbr2:int) -> int: '''Renvoie la somme de nbr1 et nbr2''' return nbr1 + nbr2

Python permet donc d'écrire le type des paramètres et de la réponse directement dans la déclaration de la fonction. La fonction help() va alors pouvoir nous ramener un peu d'informations.

>>> help(addition) Help on function addition in module __main__: addition(nbr1: int, nbr2: int) -> int Renvoie la somme de nbr1 et nbr2

11-A° Modifier alors calculer_moyenne() pour qu'elle renvoie la réponse attendue : il faudra lire la documentation pour le savoir.

1 2 3 4 5
def calculer_moyenne(note1:int, note2:int) -> float: '''Renvoie la moyenne des deux notes envoyées''' return 0.0 m = calculer_moyenne(10, 20)

Remarquez bien que pour le moment, la fonction renvoie bien une réponse du bon type (un float) mais c'est toujours 0.0 pour l'instant. A vous de faire le travail.

Exemple d'utilisation dans la console interactive (après avoir mis la fonction en mémoire !)

>>> calculer_moyenne(10, 20) 15.0 >>> calculer_moyenne(10, 14) 12.0

...CORRECTION...

1 2 3 4 5
def calculer_moyenne(note1:int, note2:int) -> float: '''Renvoie la moyenne des deux notes envoyées''' return (note1 + note2) / 2 m = calculer_moyenne(10, 20)
Une ligne ou deux lignes ?

Il est bien entendu possible d'avoir une fonction dont les instructions sont données en deux lignes :

1 2 3 4
def calculer_moyenne(note1:int, note2:int) -> float: '''Renvoie la moyenne des deux notes envoyées''' rep = (note1 + note2) / 2 return rep

Mais, est-ce que c'est bien malin de créer une variable temporaire, juste pour renvoyer sa valeur associée une ligne plus bas.

Préférez donc la version en une ligne.

1 2 3
def calculer_moyenne(note1:int, note2:int) -> float: '''Renvoie la moyenne des deux notes envoyées''' return (note1 + note2) / 2
2.3 Contenu complet d'une documentation : le docstring multiligne

A - Limitation de la description sur une ligne

Il y a parfois des cas, où une documentation basique ne suffit pas. Regardons par exemple cette fonction vitesse()

1 2 3
def vitesse(distance:int, duree:int) -> float: '''Renvoie la vitesse connaissant la distance parcourue et la durée du parcours''' return distance / duree

Si le but est de fournir un manuel d'utilisation efficace, ce n'est pas bon.

  1. Problème 1 : on ne signale pas à l'utilsateur qu'il ne doit surtout pas fournir de durée nulle. Or, la division par zéro n'est pas conseillée...
  2. Problème 2 : on ne signale pas à l'utilisateur si il y a des unités attendus sur la durée et la distance.

B - Docstring et documentation multiligne

Plutôt que de donner juste le prototype et une petite ligne d'explications, on peut donner une vraie documentation sur plusieurs lignes.

Il n'existe aucune codification de cette documentation.

Voici les éléments qu'elle doit contenir par exemple :

  • DESCRIPTION : une description de ce que fait la fonction
  • PROTOTYPE : l'équivalent de la signature et du prototype mais sous forme d'un texte plus libre
  • PRECONDITION : une explication claire des conditions supplémentaires sur les paramètres d'entrée si certaines valeurs posent problème.
  • ...

Deux contraintes uniquement :

  1. La documentation doit être tabulée comme n'importe quelle ligne appartenant à la fonction
  2. Elle doit commencer par 3 guillemets et finir par 3 guillements

11-B° Lire la nouvelle documentation de calculer_moyenne(). Répondre alors aux questions suivantes :

  • Sur quelles lignes trouve-t-on les types attendus des deux paramètres ?
  • Quelles sont les préconditions supplémentaires sur les paramètres qu'on trouve dans cette documentation plus longue ?
  • Sur quelle ligne se trouve l'indication sur le type de la réponse dans cette documentation ?
1 2 3 4 5 6 7 8 9 10 11
def calculer_moyenne(note1, note2): '''Renvoie la moyenne des deux notes :: param note1(int) :: une note dans [0;20] :: param note2(int) :: une note dans [0;20] :: return (float) :: la moyenne de note1 et note2 ''' return (note1 + note2) / 2 moy = calculer_moyenne(10, 20)

...CORRECTION...

  • Sur quelles lignes trouve-t-on les types attendus des deux paramètres ?
  • 4 5
    :: param note1(int) :: une note dans [0;20] :: param note2(int) :: une note dans [0;20]
  • Quelles sont les préconditions supplémentaires sur les paramètres qu'on trouve dans cette documentation plus longue ?
  • 4 5
    :: param note1(int) :: une note dans [0;20] :: param note2(int) :: une note dans [0;20]
  • Sur quelle ligne se trouve l'indication sur le type de la réponse dans cette documentation ?
  • 6
    :: return (float) :: la moyenne de note1 et note2

11-C ✔° Lancer le nouveau programme dans Thonny pour vérifier que la fonction fonctionne de la même façon mais que la documentation est plus fournie lorsqu'on utilise help().

Vous pouvez donc faire ceci :

>>> moy 15.0 >>> moy2 = calculer_moyenne(10, 15) >>> moy2 12.5 >>> help(calculer_moyenne) Help on function calculer_moyenne in module __main__: calculer_moyenne(note1, note2) Renvoie la moyenne des deux notes :: param note1(int) :: une note dans [0;20] :: param note2(int) :: une note dans [0;20] :: return (float) :: la moyenne de note1 et note2
1 2 3 4 5 6 7 8 9 10 11
def calculer_moyenne(note1, note2): '''Renvoie la moyenne des deux notes :: param note1(int) :: une note dans [0;20] :: param note2(int) :: une note dans [0;20] :: return (float) :: la moyenne de note1 et note2 ''' return (note1 + note2) / 2 moy = calculer_moyenne(10, 20)
Documentation Sphinx

Il existe des systèmes de documentation automatisée permettant de générer automatiquement la documentation en HTML, en PDF...

Dans ce cas, il faut respecter une certaine syntaxe dans la documentation pour que le système puisse retrouver les informations fournies efficacement.

  • :param x: derrière lequel on explique le contenu attendu pour le paramètre nommé ici x pour l'exemple
  • :type x: pour préciser le type attendu de cette variable (int ? float ? str ? bool ?)
  • :return: pour expliquer ce que va renvoyer la fonction
  • :rtype: pour préciser le type de la réponse renvoyée

Exemple avec la fonction calculer_moyenne().

1 2 3 4 5 6 7 8 9 10 11 12
def calculer_moyenne(note1, note2): '''Renvoie la moyenne des deux notes :param note1: une note dans [0;20] :type note1: int :param note2: une note dans [0;20] :type note2: int :return: la moyenne de note1 et note2 :rtype: float ''' return (note1 + note2) / 2

C'est long, c'est lourd mais c'est structuré comme le système automatique s'attend à recevoir cette documentation.

3 - Python interactif

Le programme précédent permet donc de créer la fonction calculer_moyenne() et de l'activer directement depuis le programme lui-même.

Mais la fonction calculer_moyenne() est maintenant en mémoire tant qu'on ne réappuie pas sur la flèche pour relancer un nouveau programme. Autant continuer à l'utiliser non ?

Vérifions que vous vous parvenez encore à prévoir le comportement des fonctions.

12° Vérifiez que la fonction est en mémoire. Tapez alors ceci dans la console :

>>> calculer_moyenne(12, 14) 13.0 >>> calculer_moyenne(0, 14) 7.0 >>> resultat = calculer_moyenne(0, 14) >>> resultat 7.0

Question : comparer la déclaration de la fonction et l'appel calculer_moyenne(12, 14)

1
def calculer_moyenne(note1, note2):

Que contient le paramètre note2 de notre fonction lors de l'exécution de cet appel ?

...CORRECTION...

Il suffit de comparer les places respectives des différents éléments lors de la déclaration et de l'appel.

1
def calculer_moyenne(note1, note2):
>>> calculer_moyenne(12, 14)

On voit donc que

  • 12 va être stocké dans le paramètre note1 et
  • 14 va être stocké dans le paramètre note2.

✎ 13° Compléter la fonction addition() pour qu'elle fonctionne correctement. Enregistrer et lancer dans Thonny pour placer cette fonction en mémoire. En faire l'appel ensuite dans la console pour vérifier son fonctionnement.

1 2 3 4 5 6 7 8 9
def addition(note1, note2): '''Renvoie la somme des deux notes :: param note1(int) :: une note dans [0;20] :: param note2(int) :: une note dans [0;20] :: return (int) :: la somme de note1 et note2 ''' return 0
>>> addition(12, 14) 26 >>> resultat = addition(10, 14) >>> resultat 24

✎ 14° Compléter la fonction multiplication() pour qu'elle fonctionne correctement. Enregistrer et lancer dans Thonny pour placer cette fonction en mémoire. En faire ensuite appel dans le shell pour vérifier son fonctionnement.

1 2 3 4 5 6 7 8 9
def multiplication(x, y): '''Renvoie la multiplication de a par b :: param x(int) :: un entier :: param y(int) :: un entier :: return (int) :: x*y ''' return 0
>>> multiplication(5, 10) 50 >>> resultat = multiplication(6, 6) >>> resultat 36

4 - Au return, prêt, sortez !

Que se passe-t-il lorsque la fonction rencontre un return ?

A quel moment sort-on vraiment de la fonction ?

15° Observer la fonction centaine(). On a demandé à quelqu'un de compléter la fonction suivante pour qu'elle renvoie le chiffre des centaines d'un nombre envoyé en argument. Il a tapé ceci en oubliant de supprimer la ligne qu'on lui avait donné, celle avec le return 0.

1 2 3 4 5 6 7 8 9 10
def centaine(x): '''Renvoie le chiffre de la centaine de x :: param x(int) :: un entier :: return (int) :: la centaine de x (5 si 1545) ''' return 0 mon_petit_calcul = x // 100 % 10 return mon_petit_calcul

Tester la fonction dans la console.

Questions

  1. Pourquoi cette fonction renvoie-t-elle TOUJOURS 0 ?
  2. Modifier la fonction pour qu'elle fonctionne correctement en évitant les étapes superflues.

Si vous ne voyez pas, Python Tutor est votre ami.

...CORRECTION...

On aurait pu croire que la fonction suit son cours et finit par répondre à la fin. Mais non !

Dès qu'on rencontre un return, la fonction renvoie ce qu'on a placé derrière et on sort immédiatement.

Dès qu'on arrive sur la ligne 8, on renvoie 0. Et on sort de la fonction.

Il ne sert strictement à rien de placer des choses sur des lignes qui suivent le return : ces lignes ne seront jamais analysées.

Ici, les lignes 09 et 10 ne servent strictement à rien. C'est comme si vous aviez tapé ceci :

1 2 3 4 5 6 7 8 9 10
def centaine(x): '''Renvoie le chiffre de la centaine de x :: param x(int) :: un entier :: return (int) :: la centaine de x (5 si 1545) ''' return 0

Après correction, vous devriez avoir ceci :

1 2 3 4 5 6 7 8
def centaine(x): '''Renvoie le chiffre de la centaine de x :: param x(int) :: un entier :: return (int) :: la centaine de x (5 si 1545) ''' return x // 100 % 10
4 - Espace mémoire temporaire

Dès que la fonction répond avec un return, son espace mémoire disparaît.

En conséquence, toutes les variables locales créées uniquement dans la fonction (comme les paramètres ou la variable mon_petit_calcul) disparaîssent également.

C'est comme si vous aviez tapé del mon_petit_calcul (del comme delete).

5 - Et sans return ?

Lorsqu'on rencontre le return, on sort immédiatement. Ok.

Mais...

Et si on ne rencontrait jamais de return ? Que se passe-t-il ?

Nous allons voir que la fonction répond quelque chose quand même et cette réponse est ... vide. Mais une réponse "vide" contient une information : l'information que la fonction est bien terminée puisqu'elle a répondu.

16° Observer le code suivant qui possède une fonction sans return...

1 2 3
def division(x:int, y:int) -> int: '''Calcule la division entière de x par y, mais n'en fait rien...''' mon_petit_calcul = x // y

Tester la fonction dans la console.

>>> division(7, 2) >>>

Question

  • Cette fonction renvoie-t-elle quelque chose malgré ce qu'affirme la documentation ?

...CORRECTION...

Et non. Cette fonction ne renvoie rien. On a repris la main : nous sommes donc sortis de la fonction. Mais il n'y a eu AUCUN retour.

17° Observer un peu mieux l'onglet de Thonny après avoir tapé ceci dans la console :

Questions

  • Que contient la variable reponse ?
  • Quel est le type de cette variable ?
>>> reponse = division(5, 3) >>> reponse >>> type(reponse) ???
résultat d'une fonction sans retour

...CORRECTION...

En réalité, la variable contient quelque chose !

Elle contient None.

Quel est le type de None ?

>>> reponse = division(5, 3) >>> reponse >>> type(reponse) <class 'NoneType'>

Voilà comment on pourrait interpréter le code de la fameuse fonction qui ne possède pas de return.

1 2 3 4
def division(x:int, y:int) -> NoneType: '''Calcule la division entière de x par y, mais n'en fait rien...''' mon_petit_calcul = x // y return None
5 - Pas de fonction vraiment sans retour en Python...

On retiendra donc qu'une fonction Python renvoie au moins la valeur None lorsque son code se termine sans avoir rencontré un autre return.

En Python, on parle parfois de procédure pour désigner une fonction sans return. Elle renvoie au moins None, il ne s'agit donc pas vraiment d'une vraie procédure, mais l'idée est là.

Pour finir, voici trois dernières questions qui vous permettront de voir si vous avez bien compris les notions sur les fonctions vues dans cette activité.

✎ 18° Réaliser la fonction telle qu'elle est spécifiée dans sa documentation :

1 2 3 4 5 6 7 8 9 10 11 12
def estLong(chaine): '''Fonction qui renvoie True si la chaine est d'au moins 6 caractères, False sinon :: param chaine(str) :: la chaîne de caractères :: return (bool) :: Vrai si la taille de chaine est d'au moins 6 :: exemple .. >>> estLong('Bonjour') True ''' pass

Remarque : Une fonction sans instruction provoque une erreur. L'instruction pass porte bien son nom : elle permet de fournir une instruction de façon à ne pas provoquer d'erreur de syntaxe mais cette instruction veut dire "Ne fait rien". On place donc souvent pass comme instruction temporaire.

✎ 19° Réaliser la fonction telle qu'elle est spécifiée dans sa documentation :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
def contient(chaine1, chaine2): '''Prédicat qui renvoie True si chaine1 est bien dans chaine2, False sinon :: param chaine1(str) :: la petite chaîne de caractères :: param chaine2(str) :: la grande chaîne de caractères :: return (bool) :: True si chaine1 est dans chaine2 :: exemple .. >>> contient('Bonjour', 'Bon, alors bonjour') False >>> contient('bonjour', 'Bon, alors bonjour') True ''' pass

✎ 20° Placer la fonction en mémoire. Réaliser les appels proposés.

Ecrire la documentation complète (plusieurs lignes) de cette fonction une fois que vous avez compris ce qu'elle fait.

Enfin, écrire cette fonction en utilisation une documentation rapide (prototype et une seule ligne)

Note : cette fonction utilise dans ses instructions la fonction randint() du module qui se nomme random, hasard ou aléatoire en anglais.

1 2 3 4 5 6 7 8 9 10
import random def de(nbr_faces): '''La petite phrase qui va bien :: à faire : décrire le paramètre :: :: à faire : décrire la réponse :: ''' return random.randint(1, nbr_faces)
>>> de(6) 5 >>> de(6) 6 >>> de(6) 3 >>> de(6) 4
>>> import random >>> help(random.randint) Help on method randint in module random: randint(a, b) method of random.Random instance Return random integer in range [a, b], including both end points.

6 - FAQ

J'ai entendu parler de routine, de procédure. C'est quoi ?

Le mot fonction est utilisé de façon assez générique dans Python. Par contre en informatique, il existe des entités qui peuvent être gérées de façon différente selon les langages.

Le vrai mot générique pour parler d'une suite d'instructions mémorisées et à laquelle on peut faire appel de nombreuses fois est le mot routine.

On distinguera deux types de routine :

  • Si la routine ne renvoie rien (pas de return), on parle de procédure.
  • Si la routine renvoie bien quelque chose vers le programme d'appel, on parle de fonction.

Du coup, les vraies procédures n'existent pas en Python : même si votre fonction ne renvoie rien, elle renvoie None. Cela permet de savoir que la fonction n'a rien renvoyé mais ce n'est pas vraiment ...rien... En effet, si la variable reponse existe et contient None, nous avons appris quelque chose : notre fonction est terminée.

On peut détruire une variable ?

Oui, on peut libérer la place mémoire attribuée à une variable. Pour cela, il faut utiliser le mot-clé del.

Exemple :

>>> a = 5 >>> a 5 >>> del a >>> a NameError: name 'a' is not defined

C'est tout en terme de connaissances sur les fonctions pour aujourd'hui. Si on récapitule, nous avons vu :

  • Comment déclarer une fonction
  • Comment documenter un peu les fonctions
  • Comment une fonction parvient à renvoyer quelque chose avec le mot-clé return
  • Qu'on sort IMMEDIATEMENT de la fonction dès qu'on rencontre return
  • Qu'une fonction-python renvoie au moins None.

Activité publiée le 28 08 2019
Dernière modification : 17 10 2021
Auteur : ows. h.