11B: Portée

La leçon 11 est composée de trois parties A, B et C. Ces trois parties peuvent être complétées dans n'importe quel ordre.

Dans cette leçon, nous allons expliquer un concept appelé portée des variables (anglais «variable scope»). L'idée principale est qu'il est possible d'avoir deux variables différentes avec le même nom chaque fois qu'elles ont des portées différentes. Cela rend beaucoup plus facile à écrire et éditer des programmes, en particulier les grands programmes.

Un exemple de la portée des variables

Pour cet exemple, une partie de notre programme sera la fonction suivante:

def carré(x):
   valeur = x * x
   return valeur
Ceci est une fonction qui calcule le carré du nombre x, par exemple carré(5) retourne 25. Supposons maintenant que nous appelons carré d'une autre partie du programme qui utilise aussi une variable valeur pour un autre but:

# corps principal du programme commence ici
valeur = 17
cinqcarré = carré(5)
print(cinqcarré, valeur)
La question est: qu'est-ce qui sera imprimé dans ce scénario? Il ya deux possibilités, si nous ne savons pas comment fonctionne Python:

  1. Une possibilité est que Python reconnaît que les deux valeurs des variables valeur doivent être "séparées" d’une façon ou d’une autre. Ainsi, l'appel à carré aura une valeur de retour de 25, mais la valeur de valeur dans le corps principal restera à 17, et nous voyons la sortie 25 17.
  2. L'autre possibilité est que lorsque carré est exécuté, il remplacera la valeur existante de valeur avec 25, et nous voyons la sortie 25 25.

Voyons ce qui se passe réellement!

Exemple
Introduction à la portée des variables
Nous voyons que le comportement de Python est compatible avec la possibilité #1. En fait, chaque fois vous appelez une fonction, vous ne pouvez pas affecter les variables définies en dehors de la fonction. Au lieu de cela, toutes les déclarations qui attribuent des valeurs aux variables ne concernent qu'une variable locale qui est "l'intérieur" de la fonction appelée . La raison est que Python, et la plupart des autres langues, travaillent comme ça et que cela rend beaucoup plus simple d'écrire des programmes long avec de nombreuses fonctions. En particulier, lorsque nous définissons une nouvelle fonction, nous sommes toujours autorisés à utiliser des noms de variables (y compris les variables communes comme resultat, temp et i ) à l'intérieur de la fonction, même si elles sont utilisées ailleurs pour des raisons différentes.

Illustration

Pour rendre cela plus explicite, ci-dessous est un tableau montrant les valeurs pendant qu’elles changent.

Après cette déclaration: valeurs globales valeurs locales de carré(5)
carré valeur cinqcarré x valeur
def carré(x): «+2 lignes de plus» «fnc obj»
value = 17 «fnc obj» 17
«call carr(5)» «fnc obj» 17 5
---valeur = x * x «fnc obj» 17 5 25
---return valeur «fnc obj» 17 25 5 25
print(cinqcarré, valeur) «fnc obj» 17 25

Dans le tableau, nous illustrons trois nouveaux détails:

  • La portée locale disparaît après l'appel de fonction soit terminée.
  • La façon dont nous appliquons une fonction à ses arguments est d'utiliser des variables locales! Plus précisément, lorsque nous avons appelé carré(5), c'est la même chose que la création d'un nouveau champ d'application local, en ajoutant une variable x avec la valeur 5 dans le champs local, puis en exécutant le corps de la fonction dans le champs local.
  • (Le troisième fait n'est pas lié à la portée, mais est bon à savoir.) La définition de la fonction carré est effectivement mis en œuvre par Python de la manière suivante: un objet fonction nouvelle «fnc obj» est défini, puis une variable carré est définie comme étant égale à cet objet. En résumé, les fonctions sont un type particulier de variable.

Comme d’habitude vous pouvez voir comment le programme se comporte avec l’outil de visualisation.

Exercice à choix multiple : Intérieur et extérieur
Quelle est la sortie du programme suivant?

x = "extérieur"
def xRemplace(valeur):
   x = valeur
xRemplace("intérieur")
print(x)
Correct! La déclaration x = valeur ne concerne que la version locale de la variable x à l'intérieur de la fonction. La version globale de x ne change pas. (Ainsi, la fonction xRemplace est inutile et n'a jamais d'effet.)

Un autre concept similaire à la portée est un espace de noms («namespace»). Un espace de noms est comme un champ d'application pour un paquet. Donc, même si vous importez un paquet (comme math) qui utilise le nom de la variable x quelque part, il ne se chevauchent pas avec la variable x utilisée par votre programme, car ils se situent dans différents espaces de noms.

Règles de portée : voyant vers l'extérieur

Il y a des situations où nous voulons mélanger les variables de champs locaux et globales. Un exemple courant est lorsque vous avez une variable commune initialisée une fois au début du programme, et vous voulez aussi que tout vos fonctions soient capables de lire sa valeur.

Exemple
Lecture d’une valeur globale de l’intérieur d’une fonction

Ici, la portée locale ne contient pas une variable nommée garniturePizza, mais ceci n’a pas causé une erreur . La règle Python pour les évaluations de variables stipule que si une variable qui doit être évaluée qui n'existe pas dans la portée locale, alors il cherche dans la portée globale. Dans notre cas, il a en effet trouvé garniturePizza dans la portée globale. (Dans un cas plus général le corps d'une fonction appelant une autre fonction, vous aurez trois champs; Python vérifie toujours la plus locale («localmost») d'abord et va une étape à la fois vers la portée globale jusqu'à ce que la variable soit trouvée.)

Deux des exemples ci-dessus, avec commanderPizzas et xRemplace, sont presque identiques syntaxiquement. Pourquoi Python crée une nouvelle variable locale x dans xRemplace, mais aucune nouvelle garniturePizza dans commanderPizzas? La règle de Python est la suivante: si vous lisez seulement depuis la variable, aucune nouvelle variable locale n'est créée, mais si vous écrivez dans la variable en serait-ce qu'une seule fois, alors la variable est considérée comme locale tout au long du corps de la fonction. (Ceci est la cause la plus commune de UnboundLocalError: voir ces deux questions (en anglais) [1, 2] dans la documentation officielle de Python.)

Les arguments de fonction sont toujours traités comme de nouvelles variables locales:

Exemple
Le fragment de code suivant brisé tente de réinitialiser la variable g à 0, mais il échoue.

Changements global

Comme beaucoup d'autres choses, le flux normal décrit ci-dessus fonctionne pour 99% des situations. Mais le reste, soit 1% du temps, vous voudrez peut-être vraiment de changer une variable globale dans une fonction. Python vous permet de faire cela en utilisant l'instruction global. Voici une modification de xRemplace, vu précedemment.

Exemple
Déclarer une variable comme global dans une fonction vous permet de changer la valeur globale à l'intérieur de la fonction.

Comme nous l'avons mentionné plus tôt, la lecture d'une variable globale ne nécessite pas l'aide de la déclaration «global»; seulement son écriture la nécessite.
Vous avez maintenant terminé cette leçon et vous êtes prêts à passer à la suivante.