Skip to content

Variables locales d'une fonction

Portée locale

Considérons le code suivant:

1
2
3
4
5
6
7
def f(x):
    b = 2
    return b*x

a = f(5)
print(a)
print(b)

On obtient d'abord l'affichage de la valeur de a (c'est à dire 10) puis une erreur:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
10

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-2-92f68300172b> in <module>
      5 a = f(5)
      6 print(a)
----> 7 print(b)

NameError: name 'b' is not defined

L'erreur est due au fait que nous avons cherché à accèder à b dans une zone du programme dans laquelle b n'existe pas.

L' affectation b = 2 dans la fonction définit une étiquette sur l'objet 2 (de type int) mais ce lien entre l'objet et l'étiquette n'a d'existence que localement: dès que l'on sort de la fonction, l'étiquette b n'existe plus et, en sortie de la fonction, on n'a plus d'accès à l'objet qui était ainsi étiqueté (sauf si on le renvoie avec return).

Schéma:

On a entouré b afin d'illustrer une certaine "étanchéité" entre les deux espaces (l'espace global et l'espace local à f).

Important

Toute affectation dans une fonction crée une étiquette sur un nouvel objet.
Le lien entre l'étiquette et l'objet n'existe qu'à l'intérieur de la fonction. Un appel à cette variable en dehors de la fonction provoque donc une erreur.

Variable créée extérieurement

Considérons le code suivant:

1
2
3
4
5
6
7
8
def f(x):
    b = 2
    print(c)
    return b*x


c = 42
a = f(5)

Qu'obtient-on?

solution

On obtient:

1
42

Ainsi la variable c définie à l'extérieur de f est accessible.

Variable créée extérieurement et variable locale

Avec le code suivant, qu'obtient-on? Chercher à interpréter.

1
2
3
4
5
6
7
8
def f():
    c = 666
    print(c)


c = 42
f()
print(c)
Une réponse

On obtient:

1
2
666
42

Si l'on suit l'ordre du code:

  • les lignes 1 à 3 définissent f ("affectation" de lignes d'instructions au nom f)
  • En ligne 6, on définit c (affectation de la valeur 42)
  • En ligne 7, on demande l'exécution de f: une variable locale à f de nom c est alors créée (ligne 2). Cette variable masque alors le c de la partie externe à f, seul le c local à f est accessible. L'affichage demandé en ligne 3 donne donc 666. Puis f se termine, l'étiquette locale c disparaît.
  • On exécute maintenant la ligne 8. Le c local de la fonction ayant été oublié, le seul c accessible est celui défini en ligne 6. L'affichage donne donc 42.

Important

Si une affectation a = valeur d'un objet a lieu dans une fonction f, l'étiquette a masque le temps d'exécution de la fonction f toute étiquette de nom a externe à f. Les opérations ayant lieu dans f auront donc lieu sur cette variable locale et non sur une variable externe.

Remarque

Les variables locales d'une fonction sont stockées dans un dictionnaire.

On peut observer ici les variables:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def f():
    c = 666
    a = 777
    print("f : locals:", locals())


c = 42
d = 1515
f()

print()
print("Variables dans le champ principal: ", [(n,v) for (n,v) in locals().items()  if not n.startswith('_')])
print() 

On obtient:

1
2
3
f : locals: {'c': 666, 'a': 777}

Variables dans le champ principal:  [('f', <function f at 0x7f21f5a9d1e0>), ('c', 42), ('d', 1515)]

On voit que dans f, l'étiquette c cible un entier de valeur 666, tandis que dans la partie extérieure à f, c cible un entier de valeur 42. (Au passage, on voit que f est une variable au même titre que les variables entières)

Variable créée extérieurement et variable locale, bis

Et avec le code suivant, qu'obtiendra-t-on?

1
2
3
4
5
6
7
8
9
def f(x):
    b = 2
    print(c)
    c = 666
    return b*x


c = 42
a = f(5)

Donner une explication.

Réponse

On obtient:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-4-d4286a67556f> in <module>
      7 
      8 c = 42
----> 9 a = f(5)

<ipython-input-4-d4286a67556f> in f(x)
      1 def f(x):
      2     b = 2
----> 3     print(c)
      4     c = 666
      5     return b*x

UnboundLocalError: local variable 'c' referenced before assignment

Nous n'avons donc plus accès à la variable c externe à la fonction.

Explication:

Comme nous l'avons vu, l'affectation c = 666 crée une variable locale à la fonction. L'appel à print étant fait avant l'affectation c = 666, on pourrait s'attendre qu'à ce stade, c puisse faire référence à la variable externe puis que c devienne à la ligne suivante une étiquette "locale".

Toutefois, nous constatons et retiendrons qu'à l'intérieur de la fonction, il ne peut y avoir coexistence d'une variable interne et d'une variable externe de même nom. Si ce n'était pas le cas, un code qui profiterait d'une telle possibilité serait d'ailleurs un code très peu lisible, obligeant le lecteur à une gymnastique inutile pour savoir "qui est qui".

Note

La possibilité de donner un même nom à des variables dans des fonctions différentes sans que celles-ci n'interfèrent est importante. Elle permet par exemple de nommer régulièrement i les variables de boucle, ou plus généralement de faire des choix "réguliers" pour faciliter la lecture du code.

A contrario, il peut être utile d'éviter dans les fonctions des noms d'objets "importants" du programme pour un usage local et éphémère afin de faciliter la lecture... A décider dans chaque cas particulier, la lisibilité (compréhension, maintenance du code...) devant être priviligiée dans la plupart des situations.