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.
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)
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)
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
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 moduleinconnu
comportait une modification de variablex
,y
ouz
. - 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 :
- On commence par le mot clé
def
; pour definition - On donne un nom à la fonction
- On place zéro, un, ou plusieurs paramètres entre parenthèses
- On termine par
:
- On place un corps d'instructions indentées
- On utilise le mot clé
return
pour renvoyer le résultat
Plus de détails
- 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. - 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...
- pas un mot clé, comme
- 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
- Le
:
indique que la définition de ce qui est à gauche va commencer.- soit à droite, sur une seule et même ligne (non recommandé), ...
- ... 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.
- 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 renvoieNone
- 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.
- alors implicitement
- 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 »)
- Ce quelque chose peut être
À 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.
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)!
ma_var
est ici globale,5
sera affiché- Dans la fonction
f
,ma_var
est locale et vaut7
, la fonction renvoie70
qui sera affiché ma_var
est ici globale,f
ne l'a pas modifiée,5
sera affiché à nouveau
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 :
- 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.
- Deviner si la fonction est bien définie,
- Deviner ce que renvoie l'utilisation de la fonction.
- 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
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.
- La valeur de
- La fonction est exécutée et tente de renvoyer
c
, maisc
est inconnu.- la fonction s'arrête et provoque alors une erreur.
- la fonction n'a rien renvoyé.
- 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.
- Le
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
- La fonction est bien définie, elle est simple.
- Il y a plusieurs valeurs successives à
a
- Juste avant la définition de
test_1
,a
vaut5
; ça ne compte pas ! - Au moment de l'appel,
a
vaut3
; c'est ça qui compte ! - Le paramètre
b
est donc initialisé à3
pour l'utilisation de la fonction. return b + 10
va donc renvoyer13
- Le
print(test_0(a))
va donc afficher13
- Juste avant la définition de
- 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
- La fonction est bien définie, elle fait la même chose que la précédente.
-
Il y a plusieurs valeurs successives à
a
- Le paramètre
b
est initialisé à3
pour l'utilisation de la fonction. return y
va donc renvoyer13
- Le
print(test_2(a))
va donc afficher13
- Mais ensuite,
print(y)
provoque une erreur,- en effet
y
était une variable locale, elle n'existe plus depuis quetest_2(a)
a renvoyé son résultat !
- en effet
- Le paramètre
-
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
- :danger: La fonction n'est pas bien définie.
- Il est impossible d'avoir deux paramètres de même nom.
- 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.
- 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
-
La fonction est bien définie, oui.
- On n'a pas besoin d'avoir des variables définies à ce moment.
a
etb
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.
-
Au moment de l'appel
a
, variable locale, est initialisée à3
(la valeur ded
)- la variable globale
a
dont la valeur est5
n'est plus accessible pendant l'exécution de la fonction.
- la variable globale
b
, variable locale, est initialisée à1
(la valeur dec
)c
, variable globale, pourra être utilisée dans la fonction.return 10 * a + c
va donc renvoyer le résultat de l'évaluation de10 * 3 + 1
, soit31
- 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 afficher31
- Ensuite
print(a)
affiche5
- Ce script affiche
31
, puis5
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
La fonction est bien définie, oui.
- Il faut faire attention à la variable
a
suivant où elle se trouve.
- Il faut faire attention à la variable
- 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
, soit10
- Juste après le renvoi de
10
, l'instance de la fonction est détruit avec lea
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
- Mais pendant l'exécution de la fonction
- Ce script affiche
10
puis3
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
La fonction est bien définie, oui.
- La fonction attend deux paramètres !
- 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.
- 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.
def f(x):
x = x + 5 # (1)!
return 2 * x
x = 10 # (2)!
print(f(x))
print(x) # (3)!
- La variable locale
x
est modifiée. - La variable globale
x
est crée. - La variable globale
x
n'a pas été modifiée.
30
10