Aller au contenu

Définition de fonction - Partie 2⚓︎

  • Variables locales, variables globales
  • Fonctions mal définies

Prérequis⚓︎

Il suffit d'avoir lu Définition de fonction - Partie 1

Et, donc, de comprendre ce questionnement :

Les deux scripts suivants sont-ils équivalents ?

  • Les deux fonctions renvoient le même résultat.
  • Dans le script 2, une variable y est utilisée.
🐍 Script Python
def f(n):
    # Calcul direct
    return 7 * n + 8

x, y, z = 0, 1, 2
print(x, y, z)
print(f(x), f(y), f(z))
print(x, y, z)
🐍 Script Python
def f(n):
    y = 7 * n + 8
    return y

x, y, z = 0, 1, 2
print(x, y, z)
print(f(x), f(y), f(z))
print(x, y, z)
📤 Sortie
0 1 2
8 15 22
0 1 2

On constate que le sortie est exactement la même alors que le second script « modifie une variable y ».

  • Donc la « variable y initiale » n'a pas été modifiée...
  • Nous allons expliquer ce phénomène qui est normal et salutaire.

Normal et salutaire

🐍 Script Python
from inconnu import f

x, y, z = 0, 1, 2
print(x, y, z)
print(f(x), f(y), f(z))
print(x, y, z)
  • Nous ne voulons pas que l'expression en surbrillance modifie nos variables si par hasard la fonction f d'un module inconnu comportait une modification de variable x, y ou z.
  • Nous souhaitons que si f utilise et modifie des variables, que ce soit en local, et sans conséquences globales.

Il y a donc une différence entre

  • les variables globales du script,
  • les variables locales du script.

Définition un peu plus précise⚓︎

À savoir

Pour définir une fonction :

  1. On commence par le mot clé def ; pour definition
  2. On donne un nom à la fonction
  3. On place zéro, un, ou plusieurs paramètres entre parenthèses
  4. On termine par :
  5. On place un corps d'instructions indentées
  6. On utilise le mot clé return pour renvoyer le résultat

Plus de détails

  1. Ce n'est pas la seule façon de définir une fonction. On peut aussi le faire avec lambda, mais nous ne le montrerons pas dans ce cours.
  2. Le nom de la fonction doit respecter les règles des noms de variables.
    • pas un mot clé, comme or, as, global, local, and, def, if, ...
    • ne commence pas par un chiffre
    • ne comporte que des minuscules, chiffres ou tiret-bas _ pour séparer les mots
    • techniquement les majuscules et les accents sont acceptés ; mais il ne faut pas...
  3. Les paramètres doivent être différents et être des noms de variables valides.
    • les paramètres seront des variables locales à l'intérieur de l'utilisation de la fonction
    • ils ne sont donc pas utilisables en dehors de la fonction
  4. Le : indique que la définition de ce qui est à gauche va commencer.
    • soit à droite, sur une seule et même ligne (non recommandé), ...
  5. ... soit en dessous, dans un bloc indenté (bonne méthode)
    • Le bloc indenté peut créer puis modifier des variables, dans ce cas, elles seront locales.
    • Le bloc indenté peut utiliser, sans pouvoir les modifier, des variables globales.
  6. Il peut y avoir zéro, un ou plusieurs return
    • Dès que Python en rencontre un, la fonction s'arrête
    • S'il n'y a pas d'expression après return, la fonction renvoie None
    • Sinon l'expression à droite du return est évaluée et son résultat renvoyé.
    • Si Python finit le flot d'instructions de la fonction sans rencontrer de return
      • alors implicitement return None est ajouté,
      • et donc la fonction renvoie None dans ce cas.
    • Une fonction peut provoquer une erreur durant son exécution.
    • Sinon, une fonction renvoie toujours quelque chose.
      • Ce quelque chose peut être None...
      • Par abus de langage, on dit parfois que la fonction ne renvoie rien dans ce cas.
      • Mais, de manière rigoureuse, la fonction a renvoyé None ; (pour signifier « rien »)

À savoir

Toute utilisation de fonction

  • ou bien : provoque une erreur
  • ou bien : renvoie quelque chose (qui peut être None)

Un mot sur les variables locales et globales

  • Une fonction utilise ses paramètres comme des variables locales.
  • De même, les variables créées dans la fonction seront locales.
Pourquoi locale ?
Elles ne sont pas accessibles en dehors de la fonction.

À savoir

  • Quand l'appel d'une fonction est terminé,
    • toutes les variables locales sont détruites.

Situation facile : les noms de variables sont tous différents

Variable globale
elle est accessible partout.
Variable locale
elle est accessible depuis sa création, jusqu'à la fin de la fonction.

Cette situation est trop simpliste ; dans la réalité, il peut y avoir des noms de variables en commun.

Situation normale : il y a des noms de variables en commun

Il est normal d'utiliser parfois les mêmes noms de variables dans le corps d'un script, et dans le corps d'une fonction. Le cas le plus classique est l'utilisation de i comme variable de boucle en différents endroits. On ne souhaite pas se forcer à n'utiliser que des noms différents ; on ne pourrait pas d'ailleurs le vérifier concrètement...

Bref, il faut savoir ce qu'il se passe quand une variable locale a le même nom qu'une globale.

Testons !!!

###

mapy-undvar = 5bksl-nlbksl-nldef f(x):bksl-nl mapy-undvar = 7bksl-nl return mapy-undvar py-str xbksl-nlbksl-nlprint(mapy-undvar)bksl-nlprint(f(10))bksl-nlprint(mapy-undvar)bksl-nlbksl-nlNone

A

Z

Explications en cliquant sur les puces.

🐍 Script Python
ma_var = 5

def f(x):
    ma_var = 7
    return ma_var * x

print(ma_var)   # (1)!
print(f(10))       # (2)!
print(ma_var)         # (3)!
  1. ma_var est ici globale, 5 sera affiché
  2. Dans la fonction f, ma_var est locale et vaut 7, la fonction renvoie 70 qui sera affiché
  3. ma_var est ici globale, f ne l'a pas modifiée, 5 sera affiché à nouveau
📤 Sortie
5
70
5

À savoir

  • Une variable locale ne détruit pas une variable globale de même nom à sa création, mais dans ce cas,
    • la variable globale n'est plus accessible pendant l'appel de la fonction,
    • la variable globale redevient accessible dès que l'appel de la fonction est fini.

Attention à la suite

On rappelle que dans ce cours, on n'a utilisé, pour l'instant, que des fonctions :

📋 Texte
- dont les paramètres sont des nombres ; zéro, un ou plusieurs.
- et qui renvoient `#!py None` ou bien un nombre.

Il est possible de faire des fonctions qui prennent des tableaux en paramètres et/ou qui renvoient des tableaux. Dans ce cas, les choses sont plus complexes, ce sera pour un autre cours : les fonction sur les tableaux.

Les exercices suivants sont volontairement délicats.

Objectif
Mieux comprendre le fonctionnement des variables locales et la définition d'une fonction.

Deviner ce que renvoie une fonction⚓︎

Dans chaque cas de script, répondre aux questions de tête avant de tester.

  1. Deviner si la fonction est bien définie,
  2. Deviner ce que renvoie l'utilisation de la fonction.
  3. Deviner ce qu'affiche le script.

Cas 0⚓︎

###

a = 5bksl-nlbksl-nldef testpy-und0(b):bksl-nl # return N o n ebksl-nl return cbksl-nlbksl-nla = 3bksl-nly = testpy-und0(a)bksl-nlbksl-nlNone

A

Z

Réponse, cas 0
  1. ⚠ La fonction est bien définie, oui.
    • La valeur de c est inconnue, mais Python ne s'en soucie pas ; elle pourrait être connue plus tard juste avant une autre utilisation.
    • # return N o n e est un commentaire, il est ignoré par Python.
  2. La fonction est exécutée et tente de renvoyer c, mais c est inconnu.
    • la fonction s'arrête et provoque alors une erreur.
    • la fonction n'a rien renvoyé.
  3. Une erreur est affichée : c est inconnue.
    • Le y = ... n'a pas été exécuté, le programme s'est arrêté avant l'affectation.

Cas 1⚓︎

###

a = 5bksl-nlbksl-nldef testpy-und1(b):bksl-nl return b + 10bksl-nlbksl-nla = 3bksl-nlprint(testpy-und1(a))bksl-nlbksl-nlNone

A

Z

Réponse, cas 1
  1. La fonction est bien définie, elle est simple.
  2. Il y a plusieurs valeurs successives à a
    • Juste avant la définition de test_1, a vaut 5 ; ça ne compte pas !
    • Au moment de l'appel, a vaut 3 ; c'est ça qui compte !
    • Le paramètre b est donc initialisé à 3 pour l'utilisation de la fonction.
    • return b + 10 va donc renvoyer 13
    • Le print(test_0(a)) va donc afficher 13
  3. Ce script affiche 13

Cas 2⚓︎

###

a = 5bksl-nlbksl-nldef testpy-und2(b):bksl-nl y = b + 10bksl-nl return ybksl-nlbksl-nla = 3bksl-nlprint(testpy-und2(a))bksl-nlprint(y)bksl-nlbksl-nlNone

A

Z

Réponse, cas 2
  1. La fonction est bien définie, elle fait la même chose que la précédente.
  2. Il y a plusieurs valeurs successives à a

    • Le paramètre b est initialisé à 3 pour l'utilisation de la fonction.
    • return y va donc renvoyer 13
    • Le print(test_2(a)) va donc afficher 13
    • Mais ensuite, print(y) provoque une erreur,
      • en effet y était une variable locale, elle n'existe plus depuis que test_2(a) a renvoyé son résultat !
  3. Ce script affiche 13, puis une erreur : y est inconnue.

Cas 3⚓︎

###

def testpy-und3(b, b):bksl-nl return 10 py-str a + cbksl-nlbksl-nlc, d = 3, 1bksl-nlprint(testpy-und3(c, c))bksl-nlbksl-nlNone

A

Z

Réponse, cas 3
  1. :danger: La fonction n'est pas bien définie.
    • Il est impossible d'avoir deux paramètres de même nom.
  2. Le script ne commence même pas son exécution, il s'arrête à la première phase de lecture et de tentative de définition des expressions, instructions et fonctions.
  3. Une erreur de syntaxe est affichée.

Cas 4⚓︎

###

a = 5bksl-nldef testpy-und4(a, b):bksl-nl return 10 py-str a + cbksl-nlbksl-nlc, d = 3, 1bksl-nlprint(testpy-und4(d, c))bksl-nlprint(a)bksl-nlbksl-nlNone

A

Z

Réponse, cas 4
  1. La fonction est bien définie, oui.

    • On n'a pas besoin d'avoir des variables définies à ce moment.
    • a et b seront des variables locales, dans la fonction.
    • c sera une variable globale, dans la fonction,
      • si elle est appelée, Python utilise alors la valeur au moment de l'utilisation.
  2. Au moment de l'appel

    • a, variable locale, est initialisée à 3 (la valeur de d)
      • la variable globale a dont la valeur est 5 n'est plus accessible pendant l'exécution de la fonction.
    • b, variable locale, est initialisée à 1 (la valeur de c)
    • c, variable globale, pourra être utilisée dans la fonction.
    • return 10 * a + c va donc renvoyer le résultat de l'évaluation de 10 * 3 + 1, soit 31
    • Juste après ça,
      • l'instance de la fonction est détruite avec ses variables locales,
      • la variable globale a redevient accessible.
    • Le print(test_4(d, c)) va donc afficher 31
    • Ensuite print(a) affiche 5
  3. Ce script affiche 31, puis 5 sur une autre ligne.

Cas 5 💥⚓︎

###

def testpy-und5(b):bksl-nl a = 7bksl-nl return a + bbksl-nlbksl-nla = 3bksl-nlprint(testpy-und5(a))bksl-nlprint(a)bksl-nlbksl-nlNone

A

Z

Réponse, cas 5
  1. ⚠ La fonction est bien définie, oui.
    • Il faut faire attention à la variable a suivant où elle se trouve.
  2. L'appel de la fonction se fait avec la variable globale a égal à 3
    • Mais pendant l'exécution de la fonction a sera une variable locale, car elle est aussi créée à l'intérieur.
    • b est initialisé à 3
    • Ensuite, une a locale est créée à 7
    • La fonction renvoie 7 + 3, soit 10
    • Juste après le renvoi de 10, l'instance de la fonction est détruit avec le a local.
    • Il y a ensuite l'affichage de 10
    • Enfin, l'affichage de a global, à nouveau accessible, et qui n'a pas été modifiée ; 3
  3. Ce script affiche 10 puis 3

Cas 6⚓︎

###

a = 5bksl-nlbksl-nldef testpy-und6(a, b):bksl-nl a = 7bksl-nl return a + bbksl-nlbksl-nla = 3bksl-nlprint(testpy-und6(a))bksl-nlbksl-nlNone

A

Z

Réponse, cas 6
  1. ⚠ La fonction est bien définie, oui.
    • La fonction attend deux paramètres !
  2. Le script commence son exécution, il s'arrête à l'appel test_3(a)
    • Un seul paramètre est donné ; une erreur d'exécution est provoquée.
  3. Une erreur de d'exécution est affichée.

En résumé simplifié⚓︎

Il faut comprendre les points suivants dans ce code.

Cliquer sur les bulles, l'une après l'autre.

🐍 Script Python
def f(x):
    x = x + 5  # (1)!
    return 2 * x


x = 10  # (2)!
print(f(x))
print(x)  # (3)!
  1. La variable locale x est modifiée.
  2. La variable globale x est crée.
  3. La variable globale x n'a pas été modifiée.
📋 Texte
30
10