fonctions python

Identification

Infoforall

11 - Fonctions et variables


Vous avez vu qu'il s'agit de sortes de boites noires à qui on fournit des ENTREES et que nous répondent en retour.

ENTREE(S)  ⇒   Fonction   ⇒  SORTIE

Vous savez actuellement déclarer des fonctions, faire appel à ces fonctions. Nous allons voir aujourd'hui les étudier un peu plus en détails.

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

Evaluation: 7 questions

  07-11-12-13-14-19

  20

Exercices supplémentaires 🏠 : oui

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

Documents de cours PDF : .PDF

Sources latex : .TEX et entete.tex et licence.tex

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

1 - Fonctions, paramètres, arguments

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

Rappels

(Rappel) 1.1 FONCTION : notion
ENTREE(S)  ⇒   Fonction   ⇒  SORTIE

Description

Les fonctions sont des instructions particulières qui :

  • reçoivent des données d'entrée et
  • renvoient leur réponse en 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) 1.2 - FONCTION : native

Une fonction native est une fonction présente de base dans Python.

On en fait l'appel en notant son nom et en fournissant entre parenthèses les entrées séparées par des virgules.

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

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) 1.3 FONCTION : personnelle
1 2 3 4
def double(x): # Déclaration return 2 * x # Déclaration n = double(12) # Appel

On distingue deux choses :

  1. La déclaration (mise en mémoire d'instructions pour les utiliser plus tard)
  2. L'appel (utilisation de ces instructions)
A - Déclarer une fonction

Déclarer la fonction revient à stocker ses instructions.

Pour déclarer une fonction, on écrit dans l'ordre

  1. le mot-clé def.
  2. le nom de la fonction
  3. entre parenthèses : les variables d'entrée séparées par des virgules
  4. le caractère :
  5. les instructions du bloc tabulé
1 2
def double(x): return 2 * x

Important : lors de la déclaration, on ne lance aucun appel, on ne fait que mettre en mémoire.

Le mot-clé return permet d'indiquer ce que la fonction devra renvoyer si on l'appelle.

Placer des commentaires en français dans le code Python

On peut placer du texte à destination des développeurs qui vont lire le programme en utilisant le caractère #. Voir la partie suivante pour un exemple.

Attention : sur votre ligne, tout ce qui se trouve derrière ce # sera purement ignoré par l'interpréteur.

B - Lancer un appel de fonction

Lancer un appel revient à fournir les entrées et attendre la réponse de la fonction.

Sur l'exemple, l'appel se fait LIGNE 4.

1 2 3 4
def double(x): # Déclaration return 2 * x # Déclaration n = double(12) # Appel

Déroulé

  • L1 (déclaration)
  • L4 (appel) - L1 - L2 (envoi de la réponse) - L4 (réception de la réponse) - fin

Traduction (et déroulé)

  • Ligne 1 : déclaration d'une fonction double() qui va recevoir une donnée qu'on placera dans une variable x.
  • Ligne 4 : appel à la fonction double() en lui envoyant 12
  • Ligne 1 : l'interpréteur place 12 dans x.
  • Ligne 2 : la fonction envoie la réponse 24 à la ligne qui a lancé l'appel
  • Ligne 4 : réception de la réponse et n référence 24.
C - Lancer plusieurs appels

Les fonctions ont deux avantages :

  1. Permettre l'envoi d'une sortie dont la valeur dépend des entrées reçues;
  2. Permettre plusieurs appels successifs à une même fonction.
1 2 3 4 5
def double(x): return 2 * x a = double(10) b = double(30)

Déroulé

  • L1 (déclaration)
  • L4 (appel) - L1 - L2 (envoi de la réponse) - L4 (réception de la réponse et a référence 20)
  • L5 (appel) - L1 - L2 (envoi de la réponse) - L5 (réception de la réponse et b référence 60) - fin
(Rappel) 1.4 FONCTION : plus complexe
1 2 3 4 5 6 7
def nouvelle_note(note): note = note + 5 if note > 20: note = 20 return note n = nouvelle_note(12)

Cette fonction récupère la valeur d'une note, rajoute 5, bloque la note à 20 si elle dépassait 20 et renvoie le résultat final.

Déroulé

  • L1 (déclaration)
  • L7 (appel) - L1 - L2 - L3 - L5 (envoi) - L7 (réception) - fin

Traduction et déroulé

  • Ligne 1 : déclaration de nouvelle_note() qui va recevoir une donnée qu'on placera dans note.
  • Ligne 7 (appel à la fonction nouvelle_note() en lui envoyant 12).
  • Ligne 1 à 5 : on exécute les lignes avec note contenant 12 initialement. Après la ligne 2, note contient donc 17. Le test de la condition étant alors faux (17 n'est pas supérieur à 20), on passe en ligne 5 : la fonction envoie 17 en sortie.
  • L7 (réception) : on stocke 17 dans n.

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

Quelques compléments

1.5 - FONCTION : une déclaration pas à pas
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)
  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
J'insiste mais... "Déclarer" n'est pas "Faire appel"

Il faut comprendre qu'après avoir exécuté ces lignes, l'interpréteur Python ne lance pas d'appel à votre fonction. D'ailleurs, il aurait du mal... Que mettrait-il dans 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.

1.6 return : on peut utiliser directement une expression

Lorsque l'interpréteur rencontre le mot-clé return, il réalise deux choses :

  1. Il évalue l'expression située derrière return.
  2. Il renvoie la valeur trouvée à l'endroit d'où a été lancé l'appel initialement.

Deux choix de conception :

  • Stocker d'abord le résultat dans une variable, puis placer ce nom de variable derrière return.
  • 1 2 3
    def fois2(x): resultat = x * 2 return resultat
  • Placer directement l'expression derrière return.
  • 1 2
    def fois2(x): return x * 2

Ici, il est donc inutile de stocker la réponse dans une variable puisque la seule instruction derrière serait de renvoyer le résultat stocké.

return n'est pas une fonction !

Ne placez pas de parenthèses (sauf obligation) derrière le return. Sinon, vous laissez croire qu'il s'agit d'une fonction. Il s'agit bien d'un mot-clé qui renvoie la réponse à celui qui a posé la question.

Rajoutons maintenant une couche de vocabulaire à connaître.

1.7 FONCTION : paramètre et argument
1 2 3
def valeur(d, u): # d et u sont les paramètres déclarés resultat = d*10 + u return resultat
>>> valeur(5, 3) # 5 et 3 sont les Arguments d'Appel 53

Différence entre paramètre et argument

Il faut savoir distinguer

  • La déclaration qui indique les variables locales permettant de stocker les entrées qu'on fournira un jour.
    Plutôt que de dire les variables locales de stockage d'entrées qu'on fournira un jour", on dira les paramètres.
  • 1 2 3
    def valeur(d, u): # d et u sont les paramètres déclarés resultat = d*10 + u return resultat

    Ces paramètres disparaissent donc une fois que la fonction a répondu.

  • L'appel qui fournit 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.
  • >>> valeur(5, 3) # 5 et 3 sont les Arguments d'Appel 53

    L'argument n°1 (5) va dans le paramètre n°1 (d), 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 (commence par a)

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

Précision

L'appel peut bien entendu se faire directement dans le programme. C'est même beaucoup plus courant.

1 2 3 4 5
def valeur(d, u): # d et u sont les paramètres déclarés resultat = d*10 + u return resultat valeur(5, 3) <-- 5 et 3 sont les arguments de l'appel
Paramètre formel et effectif

Il existe une deuxième manière de nommer les choses.

Paramètre formel plutôt que paramètre.

Paramètre effectif plutôt qu'argument.

Beaucoup de questions risquent de vous paraître un peu bête si vous avez vraiment compris le mécanisme des fonctions. Le but ici est bien de casser les mauvaises conceptions mentales, quitte à ouvrir des portes ouvertes.

01° Réaliser les 4 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. 1 2 3 4 5 6 7
    def fois2(x): resultat = x * 2 return resultat def plus2(x): resultat = x + 2 return resultat
  4. Vérifier via l'onglet VARIABLES que la mémoire contient maintenant deux nouvelles variables nommées fois2 et plus2.
  5. Le at 0x de l'onglet "Variables" ... fait référence à une zone mémoire de stockage et cela devrait donc être différent sur votre machine. Par contre, vous devriez avoir quelque chose de proche à l'écran :

    résultat dans thonny

  6. Utiliser ensuite la console pour visualiser que fois2 (juste le nom, sans les parenthèses) ne lance aucun appel mais spécifie que cette variable est de type function : elle référence des instructions.
  7. >>> fois2 <function fois2 at 0x7f2d96628af0> >>> type(fois2) <class 'function'>

02° On dispose de ces deux fonctions en mémoire.

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

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) ???
  1. Quel est l'argument envoyé à la fonction plus2() lors de cet appel ?
  2. Quel est le contenu du paramètre x lors de cet appel ?
  3. Quelle est la réponse de cet appel (qui s'affiche dans la console interactive) ?
>>> plus2(15) ???
  1. Quel est l'argument envoyé à la fonction plus2() lors de cet appel ?
  2. Quel est alors le contenu affecté au paramètre x lors de cet appel ?
  3. Quelle est la réponse de cet appel ?
>>> fois2(10) ???
  1. Quel est l'argument envoyé à la fonction fois2() lors de cet appel ?
  2. Quel est alors le contenu affecté au paramètre x lors de cet appel ?
  3. Quelle est la réponse de cet appel ?

...CORRECTION...

>>> plus2(10) 12
  1. Ici, on voit que l'entrée est 10.
  2. Le paramètre x référence l'entier 10.
  3. Cet appel calcule 10+2 et renvoie 12.
>>> plus2(15) 17
  1. Cette fois, on envoie 15.
  2. x référence 15.
  3. Cet appel calcule 15+2 et renvoie 17.

>>> fois2(10) 20
  1. On envoie une entrée valant 10.
  2. x référence 10.
  3. Cet appel calcule 2*10 et renvoie donc 20.

03° Un peu plus complexe que la précédente. Réaliser les appels suivants dans la console de Python, observer les réponses et puis répondre aux questions :

>>> a = plus2(100)
  1. Quel est l'argument de cet appel ?
  2. Quel est le contenu de x lors de cet appel ?
  3. Quelle est la réponse de cet appel ?
  4. Pourquoi n'obtient-on pas d'affichage dans la console ?
>>> b = fois2(a)
  1. Quel est l'argument de cet appel ?
  2. Quel est le contenu du paramètre x lors de cet appel ?
  3. Quelle est la réponse de cet appel ?
  4. Pourquoi n'obtient-on pas d'affichage dans la console ?
>>> c = fois2(plus2(10))
  1. Que va évaluer d'abord l'interpréteur Python à cause des priorités ?
  2. Quel est l'argument envoyé à la fonction fois2() ?
  3. Expliquer le contenu de c.

...CORRECTION...

>>> a = plus2(100)
  1. L'argument envoyé est 100.
  2. x contient donc 100.
  3. La fonction répond donc 102 qu'on affecte à la variable a.
  4. Ici, il y a une affectation dans une variable. La console n'affiche un résultat que si la ligne ne contient que l'évaluation.
>>> b = fois2(a)
  1. L'argument envoyé est a.
  2. x référence donc 102.
  3. La fonction répond donc 204 qu'on affecte à la variable b.
  4. Ici, il y a une affectation dans une variable. La console n'affiche un résultat que si la ligne ne contient que l'évaluation.
>>> c = fois2(plus2(10))
  1. En respectant la priorité des parenthèses, on se rend compte qu'il faut d'abord évaluer plus2(10).
  2. Python va évaluer plus2(10) à la valeur 12. On envoie donc ce 12 comme argument à fois2().
  3. 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() ???
  1. Quel est le type d'erreur ?
  2. A quoi est due cette erreur ?
  3. >>> fois2(10, 20) ???
  4. Quel est le type d'erreur ?
  5. A quoi est due cette erreur ?
  6. >>> fois2 10 ???
  7. Quel est le type d'erreur ?
  8. A quoi est due cette erreur ?
  9. >>> fois2([10]) ???
  10. Quel est le type d'erreur ?
  11. A quoi est due cette erreur ?

...CORRECTION...

    >>> fois2() TypeError: fois2() missing 1 required positional argument: 'x'
  1. C'est une erreur TypeError. En Python, il s'agit d'une erreur d'exécution.
  2. On envoie 0 argument lors de l'appel alors que la déclaration de la fonction possède UN paramètre. Il manque donc un paramètre.
  3. >>> fois2(10, 20) TypeError: fois2() takes 1 positional argument but 2 were given
  4. C'est une erreur TypeError. En Python, il s'agit d'une erreur d'exécution.
  5. On envoie 2 arguments lors de l'appel alors que la fonction possède UN paramètre.
  6. >>> fois2 10 SyntaxError: invalid syntax
  7. C'est une erreur de SYNTAXE. Python ne comprend pas l'appel.
  8. L'utilisateur a oublié les parenthèses nécessaires en Python lors de l'appel.
  9. >>> fois2([10]) [10, 10]
  10. Par d'erreur en soi : pas d'erreur de syntaxe, ni d'erreur d'exécution.
  11. 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 1 case. Python fait son travail, il renvoie un tableau de deux cases identiques.

  12. 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.

05° Répondre à trois ensembles de questions ci-dessous.

A - AVANT d'avoir exécuter quoi que ce soit

Fournir la succession des lignes suivies par l'interpréteur Python lors de l'exécution de ce programme.

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)

B - Python Tutor

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

C - APRES Python Tutor

  1. Lorsqu'on exécute la ligne 1, met-on la fonction en mémoire ou lance-t-on la fonction ?
  2. Les variables d et u existent-elles dans l'espace des noms global ?
  3. Que devient son espace des noms local après que la fonction ai répondu ?
  4. Les variables a, b et c sont-elles des variables locales ou globales ?

...CORRECTION...

Partie A

L1 (déclaration)

L5 (appel en envoyant 2 et 3) - L1 - L2 - L3(réponse)

L5 (réception puis affectation)

L6 (appel en envoyant 2 et 3) - L1 - L2 - L3(réponse)

L6 (réception puis affectation)

L7 (appel en envoyant 2 et 3) - L1 - L2 - L3(réponse)

L7 (réception puis affectation)

Fin

Partie C

  1. On place juste la fonction en mémoire. C'est une déclaration, pas un appel.
  2. Chaque appel de fonction génère son propre espace des noms local et temporaire lui permettant de stocker les arguments reçus dans ses propres paramètres. d et u sont des variables locales.

  3. Dès qu'une fonction a répondu, l'espace des noms local de cet appel est supprimé puisqu'il ne sert plus à rien.
  4. Les variables a, b et c sont globales puisqu'elles sont déclarées directement dans le programme.

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

Question

Expliquer la valeur 26 obtenue sur cet appel en montrant comment on compare l'appel et la déclaration.

...CORRECTION...

1
def valeur(d, u):
>>> valeur(2, 6)

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.

07° Expliquer la réponse qu'on va obtenir lors de l'appel de la fonction facile() puis plus_difficile() ci-dessous. On vous donne leurs déclarations de la fonction et l'appel voulu dans la console interactive.

Première fonction

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

Deuxième fonction

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

...CORRECTION...

Le 3 est stocké dans la variable locale x. La première fonction calcule donc 3*10+5, et renvoie 35.

En comparant appel et déclaration pour la deuxième fonction, on voit que x reçoit 5, y reçoit 6 et z reçoit 3. La fonction calcule 500+60+3 et renvoie 563.

08° 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. Expliquer les valeurs finales stockées 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 possibilité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

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 toujours le bon résultat
  • On en parlera dans la partie ALGORITHMIQUE.

  • Point 2 : ensuite des fonctions facilement utilisables
  • On en parlera un peu aujourd'hui avec la notion de DOCUMENTATION.

  • Point 3 : ensuite des fonctions dont le code interne est facile à comprendre
  • On en parlera un peu aujourd'hui avec la notion de COMMENTAIRE.

  • Point 4 : enfin, des fonctions qui répondent rapidement
  • On en parlera dans la partie ALGORITHMIQUE.

(Rappel) 2.1 COMMENTAIRES : expliquer le fonctionnement

Les commentaires sont destinés à un lecteur humain et ils visent à rendre le code interne facile à comprendre.

Cela doit permettre de modifier un code même plusieurs années après sa création initiale.

Pour rajouter un commentaire en Python, on utilise le caractère dièse (#) de façon adaptée. Trois exemples à parfaitement comprendre :

  • Commentaire sur toute une ligne (ligne 1 ci-dessous)
  • 1
    # Toute cette ligne est un commentaire.
  • Commentaire en fin de ligne (ligne 2 ci-dessous)
  • 2
    print("Bonjour tout le monde") # Ceci est également un commentaire
  • Notez bien que la ligne 3 ne comporte aucun commentaire puisque le # fait juste partie d'un string.
  • 3
    print("Cette ligne ne contient pas de # commentaire")

Voici le résultat de ce programme L1 à L3 dans la console : il n'affiche pas les commentaires.

1 2 3
# Toute cette ligne est un commentaire. print("Bonjour tout le monde") # Ceci est également un commentaire print("Cette ligne ne contient pas de # commentaire")
>>> %Run progcommentaires.py Bonjour tout le monde Cette ligne ne contient pas de # commentaire >>>

L'interpréteur Python ne tentera pas d'exécuter les commentaires.

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

2.2 DOCUMENTATION : expliquer l'utilisation

2.2.1 Documenter pour utiliser

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)
2.2.2 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 len() : 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(len) Help on built-in function len in module builtins: len(obj, /) Return the number of items in a container.

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

Help on built-in function len in module builtins:

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

len(obj, /)

Troisième ligne : on voit que la fonction va renvoyer le nombre d'éléments contenus dans le conteneur envoyé.

Return the number of items in a container.
2.2.3 Contenu d'une documentation

La documentation contient juste des informations sur

  1. Le but de cette fonction
  2. le type et le nombre d'arguments à lui envoyer (les ENTREES) et
  3. le type et le contenu de la réponse (la SORTIE).

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

>>> help(bin) Help on built-in function bin in module builtins: bin(number, /) Return the binary representation of an integer. >>> bin(2796202) '0b1010101010101010101010'

Nous verrons bientôt à quoi correspond le / dans la documentation. Pour l'instant, faisons comme s'il n'était pas là.

Questions

  1. Dans la documentation, où est-il indiqué que bin() est une fonction native ?
  2. combien d'argument(s) doit-on envoyer à bin() ? De quel(s) type(s) ?
  3. En traduisant la phrase de description, expliquer ce que renvoie la fonction (le type et le contenu de la réponse).

...CORRECTION...

  1. Il est noté que bin() est une fonction build-in, "construire à l'interne" en traduction mot à mot.
  2. Un seul de type int. C'est décrit de deux façons : une indication dans la phrase de description et une indication dans l'exemple d'utilisation qu'on trouve en bas de documentation.
  3. La fonction renvoie une représentation de l'entier fourni sous forme binaire, fournie via un string.

Nous allons maintenant voir comment écrire les documentations de vos fonctions personnelles.

2.3 DOCUMENTATION rapide : le prototype + une phrase
1 2 3
def addition(nbr1:int, nbr2:int) -> int: """Renvoie la somme de nbr1 et nbr2""" return nbr1 + nbr2

Nous avions vu la signature d'un opérateur (comme +) : int + int -> int

Nous allons voir le même principe pour les fonctions.

2.3.1 - Signature d'une fonction
Arguments  ⇒   Fonction   ⇒  Réponse

Pour pouvoir utiliser une fonction, on a besoin :

  1. de savoir comment elle se nomme.
  2. de savoir combien d'arguments lui envoyer et leurs types.
  3. de savoir ce qu'elle va répondre.

Toute ces informations se retrouvent dans la signature. Par exemple, la signature d'une fonction addition() qui permet d'additionner deux entiers :

addition(int, int) -> int

2.3.2 - Prototype d'une fonction

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

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 deviner. On peut donc se tromper.

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

Spécifier une fonction consiste à donner :

  1. Le PROTOTYPE qui fixe la SYNTAXE et le TYPAGE
  2. Une description nommée DOCSTRING qui fixe clairement la SEMANTIQUE

Sous la première ligne du prototype, on rajoute donc un string explicatif :

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

Exemple

Voilà la spécification de notre fonction-exemple :

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

La fonction help() renvoie les informations fournies dans la documentation.

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

10-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 vos modifications de 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)
2.4 DOCUMENTATION longue : le docstring multiligne

2.4.1 Limitation de la documentation rapide

Parfois, la documentation rapide ne suffit pas. 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""" return distance / duree
  1. Problème 1 : on ne signale pas à l'utilisateur qu'il ne doit surtout pas fournir de durée nulle pour éviter une division par zéro.
  2. Problème 2 : on ne signale pas si il y a des unités attendues pour durée et distance.
2.4.2 Solution : Docstring multiligne

On peut fournir une vraie documentation sur plusieurs lignes.

Il n'existe aucune codification imposée pour cette documentation. Voici les éléments qu'elle doit contenir, quelque soit la façon de l'exprimer :

  • DESCRIPTION : une description de ce que fait la fonction
  • PROTOTYPAGE : 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.
  • ...
1 2 3 4 5 6 7 8 9
def vitesse(distance, duree): """Renvoie la vitesse connaissant la distance parcourue et la durée :: param distance(int) :: la distance en m :: param duree(int) :: la durée NON NULLE en s :: return (float) :: la vitesse en m.s-1 """ return distance / duree

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

  1. Sur quelles lignes trouve-t-on les types attendus des deux paramètres ?
  2. Quelles sont les préconditions qu'on trouve dans cette documentation plus longue ?
  3. 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)

Enfin, lancer le programme dans Thonny pour vérifier que la fonction fonctionne de la même façon que celle de la question précédente mais que la documentation est juste plus fournie lorsqu'on utilise help().

>>> 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

...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 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° Compléter les fonctions addition(), multiplication() et division_euclidienne() pour qu'elles fonctionnent correctement. Enregistrer et lancer dans Thonny pour placer cette fonction en mémoire. Faire les appels dans la console pour vérifier le fonctionnement.

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
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 def multiplication(x, y): """Renvoie la multiplication de x par y :: param x(int) :: un entier :: param y(int) :: un entier :: return (int) :: x*y """ return 0 def division_euclidienne(a, b): """Renvoie le quotient et le reste de la division euclidienne de a par b :: param a(int) :: un entier :: param b(int) :: un entier NON NUL :: return (tuple) :: le couple (quotient, reste) """ return (0, 0)
>>> addition(12, 14) 26 >>> resultat = addition(10, 14) >>> resultat 24 >>> multiplication(5, 10) 50 >>> resultat = multiplication(6, 6) >>> resultat 36 >>> division_euclidienne(72, 10) (7, 2)

...CORRECTION...

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
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 note1 + note2 def multiplication(x, y): """Renvoie la multiplication de x par y :: param x(int) :: un entier :: param y(int) :: un entier :: return (int) :: x*y """ return x * y def division_euclidienne(a, b): """Renvoie le quotient et le reste de la division euclidienne de a par b :: param a(int) :: un entier :: param b(int) :: un entier NON NUL :: return (tuple) :: le couple (quotient, reste) """ return (a // b, a % b)

12° Placer la fonction en mémoire. Réaliser les appels proposés pour comprendre ce que fait cette fonction.

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

Question B : écrire la documentation en utilisation une documentation rapide (prototype et une seule ligne).

1 2 3 4 5 6 7 8 9 10
import random def de(nb_faces): '''La petite phrase qui va bien :: à faire : décrire le paramètre :: :: à faire : décrire la réponse :: ''' return random.randint(1, nb_faces)
>>> de(6) 5 >>> de(6) 6 >>> de(6) 3 >>> de(6) 4 >>> de(20) 18 >>> de(20) 3 >>> de(20) 20 >>> de(20) 7
>>> 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.

...CORRECTION...

1 2 3 4 5 6 7 8 9 10
import random def de(nb_faces): '''Renvoie un nombre aléatoire entre 1 et nb_faces inclus. :: nb_faces(int) : valeur maximale voulue pour le dé :: :: return (int) : un entier aléatoire dans l'intervalle [1; nb_faces] :: ''' return random.randint(1, nb_faces)
1 2 3 4 5
import random def de(nb_faces:int) -> int: '''Renvoie un nombre aléatoire entre 1 et nb_faces inclus.''' return random.randint(1, nb_faces)

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

1 2 3
def est_long(chaine:str) -> bool: """Fonction qui renvoie True si la chaine est d'au moins 6 caractères, False sinon""" 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.

...CORRECTION...

1 2 3
def est_long(chaine:str) -> bool: """Fonction qui renvoie True si la chaine est d'au moins 6 caractères, False sinon""" return len(chaine) >= 6

14° 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
def contient(chaine1:str, chaine2:str) -> bool: """Prédicat qui renvoie True si chaine1 est bien dans chaine2, False sinon :: exemple .. >>> contient('Bonjour', 'Bon, alors bonjour') False >>> contient('bonjour', 'Bon, alors bonjour') True """ pass

...CORRECTION...

1 2 3 4 5 6 7 8 9 10 11
def contient(chaine1:str, chaine2:str) -> bool: """Prédicat qui renvoie True si chaine1 est bien dans chaine2, False sinon :: exemple .. >>> contient('Bonjour', 'Bon, alors bonjour') False >>> contient('bonjour', 'Bon, alors bonjour') True """ return chaine1 in chaine2

3 - Return, ou pas Return, 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
3.1 FONCTION : sortie immédiate avec return

Dès que la fonction rencontre un return :

  1. elle évalue l'expression fournie puis
  2. elle renvoie sa réponse, puis
  3. son espace local des noms est détruit définitivement.

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): """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) >>>

Puisque division(7, 2) est la seule chose présente sur la ligne, sa réponse aurait du être transmise dans la console. Or, on constate que rien ne s'affiche. En apparence, la fonction n'a rien répondu.

Voici la suite :

>>> reponse = division(5, 3) >>> reponse >>> type(reponse) ???
résultat d'une fonction sans retour

Questions

  1. Que contient la variable reponse ?
  2. Quel est le type de cette variable ?

...CORRECTION...

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

Elle contient None.

Quel est le type de None ? NoneType dont la seule valeur possible est None.

>>> reponse = division(5, 3) >>> reponse >>> type(reponse) <class 'NoneType'>
3.2 FONCTION : return None

3.2.1 Le cas du return absent

Une fonction Python renvoie au moins la valeur None lorsqu'elle se termine sans avoir rencontré return.

On parle parfois de procédure pour désigner une fonction qui ne répond rien. Les procédures n'existent donc pas vraiment en Python.

La ligne 4 ci-dessous est donc totalement facultative :

1 2 3 4
def division(x:int, y:int) -> None: """Calcule la division entière de x par y, mais n'en fait rien...""" mon_petit_calcul = x // y return None
3.2.2 Attention au stockage de la réponse...

Il arrive souvent qu'on utilise mal de telles fonctions qui renvoient None si on a lu trop vite la documentation.

Le cas typique vient de la méthode append() qui permet de rajouter une case à la fin d'un tableau Python : elle modifie le tableau mais ne renvoie "rien".

Bonne utilisation

>>> t = [10, 20, 30] >>> t.append(100) >>> t [10, 20, 30, 100]

Mauvaise utilisation

Si quelqu'un l'utilise mal en pensant qu'elle répond en donnant la nouvelle version du tableau, il va écraser le tableau et y placer None !

>>> t = [10, 20, 30] >>> t = t.append(100) >>> t

Ici, t contient maintenant None.

Attention : si on utilise append(), il ne s'agit plus d'un tableau statique mais d'un tableau dynamique. Nous les utiliserons plus tard dans l'année.

3.2.3 None et les booléens

Lorsqu'on veut évaluer un contenu sous forme de booléen, None est considéré comme le cas nul ou vide.

>>> bool(None) False

Mais attention : None n'est identique à False.

>>> a = None >>> a == False False >>> bool(a) == False True

4 - Compléments sur les paramètres

Cette partie ne comporte aucun attendu du programme de NSI.

Par contre, elle vous permettra de mieux comprendre certains codes que vous pourriez trouver sur le Web et vous simplifier la vie lors de vos projets.

4.1 FONCTION : Paramètres par défaut

4.1.1 Intérêt

Imaginons une fonction qui renvoie un résultat aléatoire compris entre un entier debut et un entier fin :

1 2 3 4 5 6 7 8 9 10 11
import random def aleatoire(debut, fin): """Renvoie un entier aléatoire entre debut et fin :: param debut(int) :: un entier :: param fin(int) :: un entier SUPERIEUR à debut :: return (int) :: un entier dans [debut, fin] """ return random.randint(debut, fin)

Si cette fonction doit simuler un dé, l'entier debut sera toujours 1. Et pourtant, il faut toujours lui fournir cette valeur puisqu'il y a deux arguments à envoyer.

Si on ne met qu'un seul paramètre attendu et qu'on impose que la valeur de départ est toujours 1, on obtient une fonction moins flexible.

Comment avoir le meilleur des deux mondes ?

4.1.2 Paramètres par défaut

On peut donner des valeurs d'arguments par défaut aux fonctions. Si l'utilisateur ne fournit pas de valeurs pour ces paramètres, il sera rempli automatiquement avec la valeur située un signe = dans la déclaration.

1 2 3 4 5 6 7 8 9 10 11
import random def aleatoire(fin, debut=1): """Renvoie un entier aléatoire entre debut et fin :: param fin(int) :: un entier SUPERIEUR à debut :: param debut(int) :: un entier :: return (int) :: un entier dans [debut, fin] """ return random.randint(debut, fin)

On peut alors faire des appels en envoyant la valeur de départ, ou pas.

>>> aleatoire(50, 40) 43 >>> aleatoire(50) 12

Attention au positionnement : remarquez bien qu'il faut que les paramètres ayant une valeur par défaut soient positionnés derrière les paramètres sans valeur par défaut. C'est pour cela que debut=1 est déclarée derrière fin.

4.1.3 Documentation

On peut bien entendu continuer à indiquer rapidement le type des paramètres, même avec des valeurs par défaut.

1 2 3 4 5
import random def aleatoire(debut:int=1, fin:int=1): """Renvoie un entier aléatoire entre debut et fin""" return random.randint(debut, fin)
4.2 FONCTION : Paramètres nommés ou positionnels

4.2.1 Paramètres nommés

Cette fonctionnalité est présente de base dans Python : plutôt que de fournir les arguments dans l'ordre imposé par l'ordre des paramètres dans la déclaration, on peut les fournir comme on le veut, pourvu de tous les fournir.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
import random def somme_aleatoire(nb, debut, fin): """Renvoie la somme de x lancés aléatoire entre debut et fin :: param nb(int) :: POSITIF, le nombre de lancés voulus :: param debut(int) :: un entier :: param fin(int) :: un entier SUPERIEUR à debut :: return (int) :: un entier dans [nb*debut, nb*fin] """ s = 0 # Compteur-mémoire de la somme for _ in range(nb): # Fait l'action nb fois s = s + random.randint(debut, fin) # Incrémente s d'un nombre aléatoire return s
>>> somme_aleatoire(10, 1, 6) # on demande 10 dés à 6 faces >>> somme_aleatoire(nb=10, debut=1, fin=6) # on demande 10 dés à 6 faces >>> somme_aleatoire(debut=1, fin=6, nb=10) # on demande 10 dés à 6 faces
4.2.2 Paramètres positionnels uniquement

Nommer les paramètres, c'est pratique mais dangereux. Un changement de nom et tous les programmes utilisant l'ancienne version ne fonctionneront plus.

On peut empêcher l'utilisation des paramètres nommés et donc imposer que seul l'ordre de transfert des arguments soit important.

Pour cela, il suffit de rajouter un symbole / dans la liste des paramètres.

1 2 3 4
import random def somme_aleatoire(nb, /, debut, fin): ...
  • Les paramètres à gauche du / sont positionnels uniquement (nb).
  • Les paramètres à droite du / sont nommables lors de l'appel (debut et (fin)).
>>> somme_aleatoire(10, 1, 6) # on demande 10 dés à 6 faces >>> somme_aleatoire(10, debut=1, fin=6) # on demande 10 dés à 6 faces >>> somme_aleatoire(nb=10, debut=1, fin=6) # ECHEC : on tente de nommer le premier paramètre ! TypeError: somme_aleatoire() got some positional-only arguments passed as keyword arguments: 'nb'
4.3 FONCTION : documentation des types construits

Selon les versions de Python, il est possible, ou pas, d'écrire ce que contient un type construit.

Exemple avec une fonction dont le paramètre doit être un tableau d'entiers.

1 2
def f(x:list[int]): ...

Pour un dictionnaire dont les clés sont des strings et les valeurs sont des integers :

1 2
def f(x:dict[(str, int)]): ...

Si la version de Python que vous utilisez provoque une erreur et que vous ne pouvez pas en changer pour une raison ou une autre, il suffit de modifier la documentation pour fournir qu'un string :

1 2
def f(x:"dict[(str, int)]"): ...

5 - Exercices

17° Répondre aux questions suivantes liées au programme fourni :

1 2 3 4 5
def f(a:int, b:int) -> int: return (a+b)*10 x = f(2, 6) y = f(1, x)
  1. Donner la succession de lignes suivies par l'interpréteur sur ce programme.
  2. Quels sont les arguments envoyés à la fonction f lors de l'appel de la ligne 5 ?
  3. A quoi fait référence le paramètre a lors de cet appel ?
  4. A quoi fait référence le paramètre b lors de cet appel ?
  5. Que contient la variable globale y après avoir reçu la réponse de la fonction ?

...CORRECTION...

  1. L1(déclaration) - L4(appel) - L1(réception des arguments) - L2(envoi) - L4(réception et stockage) - L5(appel) - L1(réception des arguments) - L2 (envoi) - L5(réception et stockage) - fin.
  2. Pour la suite des questions, le plus facile est de mettre en concordance la ligne de la déclaration et la ligne de l'appel.

    1 5
    def f(a, b): y = f(1, x)
  3. La ligne 5 permet de voir qu'on envoie les arguments 1 et x à notre fonction.
  4. Le paramètre a va donc faire référence à 1.
  5. Le paramètre b va donc faire référence à x. On peut donc dire que b_de_la_fonction et x_du_programme sont des alias du même contenu mémoire : 80
  6. Il suffit donc de faire le calcul (1+80)*10, soit 810. La variable y fait donc référence à 80 une fois que la fonction a répondu une nouvelle fois.

18° Fournir la succession de lignes suivies par l'interpréteur pour ce programme :

1 2 3 4 5 6 7
import random def f(a:int) -> int: return a * random.randint(1, 10) x = f(2, 6) y = f(1, x)

...CORRECTION...

L1 (importation)

L3(déclaration)

L6(appel) - L3(réception des arguments) - L4(envoi) - L6(réception et stockage)

L7(appel) - L3(réception des arguments) - L4 (envoi) - L7(réception et stockage).

Fin

19° Répondre aux questions suivantes liées au programme fourni :

1 2 3 4 5
def m(a:int, b:int): return (a+b)/2 x = m(2, 6) a = m(10, 20)
  1. Ligne 4
    • Quels sont les arguments envoyés à la fonction m lors de cet appel ?
    • A quoi fait référence le paramètre a lors de cet appel ?
    • A quoi fait référence le paramètre b lors de cet appel ?
    • A quoi fait référence la variable globale x après cet appel ?
  2. Ligne 5
    • Quels sont les arguments envoyés à la fonction m lors de cet appel ?
    • A quoi fait référence le paramètre a lors de cet appel ?
    • A quoi fait référence le paramètre b lors de cet appel ?
    • A quoi fait référence la variable globale a après cet appel ?

...CORRECTION...

20° Donner la ligne et le nom de la seule variable globale de ce programme.Fournir ensuite la succession des lignes suivies lors de l'exécution de ce programme. Enfin, fournir le contenu récupérée dans la variable globale.

1 2 3 4 5 6 7
def h(x:int, y:int): return x+y def m(a, b): return h(a:int, b:int) * 1000 x = m(2, 6)

...CORRECTION...

La seule variable globale est la variable x de la ligne 7, qui n'a rien à voir avec la variable locale de la ligne 1.

L1(déclaration) - L4(déclaration) - L7(appel en envoyant 2 et 6) - L4(réception de 2 dans a et 6 dans b) - L5(appel en envoyant a et b donc 2 et 6) - L1(réception de 2 dans x locale et 6 dans y locale) - L2(calcul de 2+6=8 et envoi du 8) - L5(retour du 8, calcul de 8*1000=8000 et envoi du 8000) - L7(retour du 8000 qui est alors référencé par la variable x globale).

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 : 15 07 2023
Auteur : ows. h.