Laissons de coté le traitement du HTML une minute pour parler de la manière dont Python gère les variables. Python a deux fonctions prédéfinies permettant d’accéder aux variables locales et globales sous forme de dictionnaire : locals et globals.
Vous vous rappelez de locals ? Vous l'avez vu pour la première fois ici :
Mais vous ne pouvez rien apprendre sur locals pour le moment. Vous devez d'abord apprendre les espaces de noms. C’est un concept un peu aride mais important, lisez donc
attentivement.
Python utilise ce que l’on appelle des
espaces de noms pour suivre les variables. Un espace de noms est
semblable a un dictionnaire dans lequel les clés sont les noms des
variables et les valeurs du dictionnaire sont les valeurs des variables.
En fait, on accède à un espace de noms comme à un dictionnaire
Python, comme nous le verrons un peu plus
loin.
A n’importe quel point dans un programme
Python, il y a plusieurs espaces de noms
disponibles. Chaque fonction a son propre espace de noms, appelé espace
de noms local, qui suit les variables de la fonction, y compris ses
arguments et les variables définies localement. Chaque module a son
propre espace de noms, appelé l’espace de noms global, qui suit les
variables du module, y compris les fonctions, les classes, les modules
importés et les variables et constantes du module. Il y a également un
espace de noms prédéfini, accessible de n’importe quel module et qui
contient les fonctions et exceptions du langage.
Lorsqu’une ligne de code demande la valeur d’une variable
x, Python recherche cette
variable dans tous les espaces de noms disponibles dans l’ordre suivant
:
- Espace de noms local - spécifique à la fonction ou méthode de
classe en cours. Si la fonction a défini une variable locale
x, ou si elle a un argument x,
Python l’utilise et arrête sa
recherche.
- Espace de noms global - spécifique au module en cours. Si le
module a défini une variable, une fonction ou une classe nommée
x, Python l’utilise et
arrête sa recherche.
- Espace de noms prédéfini - global à tous les modules. En
dernière instance, Python considère que
x est le nom d’une fonction ou variable du
langage.
Si Python ne trouve
x dans aucun de ces espaces de noms, il abandonne et
déclenche une exception NameError avec le message
There is no variable named 'x', que vous avez vu
tout au début au chapitre
1, mais à ce moment là vous ne pouviez pas savoir tout le travail
que Python fait avant de vous renvoyer cette
erreur.
 |
Python 2.2 a introduit une
modification légère mais importante qui affecte l’ordre de recherche
dans les espaces de noms : les portées imbriquées. Dans les versions
précédentes de Python, lorsque vous
référenciez une variable dans une fonction imbriquée ou une fonction lambda,
Python recherchait la variable dans l’espace
de noms de la fonction (imbriquée ou lambda) en
cours, puis dans l’espace de noms du module.
Python 2.2 recherche la variable dans
l’espace de noms de la fonction (imbriquée ou
lambda) en cours, puis dans l’espace de
noms de la fonction parente, puis dans l’espace de noms du
module. Python 2.1 peut adopter l'un ou l'autre de ces
comportements, par défaut il fonctionne comme
Python 2.0, mais vous pouvez ajouter la
ligne de code suivante au début de vos modules pour les faire
fonctionner comme avec Python 2.2 :
from __future__ import nested_scopes |
Vous êtes perdu ? Ne vous inquiétez pas, je vous promet que c'est très utile. Comme beaucoup de chose en Python, les
espaces de noms sont directement accessibles durant l’exécution.
L’espace de noms local est accessible par la fonction prédéfinie
locals et l’espace de noms global (du module) est
accessible par la fonction prédéfinie globals.
Exemple 8.10. Présentation de locals
>>> def foo(arg):
... x = 1
... print locals()
...
>>> foo(7)
{'arg': 7, 'x': 1}
>>> foo('bar')
{'arg': 'bar', 'x': 1}
|
La fonction foo a deux variables dans
son espace de noms local : arg, dont la valeur
est passée à la fonction et x, qui est définie
dans la fonction.
|
|
locals retourne un dictionnaire de
paires nom/valeur. Les clés du dictionnaire sont les noms des
variables sous forme de chaînes, les valeurs du dictionnaire sont
les valeurs des variables. Donc, appeler foo
avec 7 affiche un dictionnaire contenant les
deux variables locales de la fonction : arg
(7) et x
(1).
|
|
Rappelez-vous que Python est à
typage dynamique, vous pouvez donc passer une chaîne pour
arg, la fonction (et l’appel à
locals) fonctionne tout aussi bien.
locals fonctionne avec toutes les variables
de tous les types.
|
Ce que fait locals pour l’espace de noms
local (de la fonction), globals le fait pour
l’espace de noms global (du module). globals est
cependant plus intéressant, parce que l’espace de noms d’un module est
plus intéressant. L’espace de noms d’un module ne comprend pas seulement
les variables et constantes du module mais aussi l’ensemble des
fonctions et classes définies dans le module. De plus, il contient tout
ce qui a été importé dans le module.
Vous rappelez-vous de la différence entre from module import et import module ? Avec
import module, le module
lui-meme est importé mais il garde son propre espace de noms, c’est
pourquoi vous devez utiliser le nom du module pour accéder à ses
fonctions ou attributs :
module.fonction.
Mais avec from module import, vous importez des fonctions et des attributs
spécifiques d’un autre module dans votre propre espace de noms, c’est
pourquoi vous y accédez directement sans référence au module dont ils
viennent. Avec la fonction globals, vous pouvez
voir ce qui se passe.
Exemple 8.11. Présentation de globals
Regardez me bloc de code suivant, à la fin de BaseHTMLProcessor.py:
if __name__ == "__main__":
for k, v in globals().items():
print k, "=", v
|
Ne soyez pas intimidé, vous avez déjà vu tout cela. La
fonction globals retourne un dictionnaire et
nous parcourons le
dictionnaire à l’aide de la méthode
items et de l’assignement multiple. Le
seul élément nouveau ici est la fonction
globals.
|
Maintenant, exécuter le programme de la ligne de commande nous
donne la sortie suivante (notez qu'elle peut être légèrement différente en fonction de votre plate-forme et de l'endroit
où vous avez installé Python) :
c:\docbook\dip\py> python BaseHTMLProcessor.py
SGMLParser = sgmllib.SGMLParser
htmlentitydefs = <module 'htmlentitydefs' from 'C:\Python23\lib\htmlentitydefs.py'>
BaseHTMLProcessor = __main__.BaseHTMLProcessor
__name__ = __main__
... rest of output omitted for brevity...
|
SGMLParser a été importé de sgmllib, en utilisant from module import. Cela veut dire
qu’il a été importé directement dans l’espace de noms du module,
et nous le voyons donc s’afficher.
|
|
Comparez avec htmlentitydefs, qui a été importé
avec import. Le module htmlentitydefs lui-même est dans
notre espace de noms, mais la variable
entitydefs définie dans htmlentitydefs ne l’est pas.
|
|
Ce module ne définit qu’une classe,
BaseHTMLProcessor et la voici. Notez que
la valeur est ici la classe
elle-même et non une instance quelconque de cette
classe.
|
|
Vous rappelez-vous de l’astuce if
__name__ ? Lorsque vous
exécutez un module (plutôt que de l’importer d’un autre module),
l’attribut prédéfini __name__ a une valeur
spéciale, __main__. Comme nous avons exécuté ce
module comme un programme de la ligne de commande,
__name__ vaut __main__,
c’est pourquoi notre petit code de test qui affiche
globals est exécuté.
|
 |
| A l’aide des fonctions locals et
globals, vous pouvez obtenir la valeur d’une
variable quelconque dynamiquement, en fournissant le nom de la
variable sous forme de chaîne. C’est une fonctionnalité semblable à
celle de la fonction getattr, qui
vous permet d’accéder à une fonction quelconque dynamiquement en
fournissant le nom de la fonction sous la forme d’une chaîne.
|
Il y a une différence importante entre locals
et globals que vous devez apprendre maintenant pour
ne pas qu’elle vous joue des tours plus tard. Elle vous jouera des tours
de toute manière mais au moins vous vous souviendrez que vous l’avez
appris.
Exemple 8.12. locals est en lecture seule, globals ne l'est pas
def foo(arg):
x = 1
print locals()
locals()["x"] = 2
print "x=",x
z = 7
print "z=",z
foo(3)
globals()["z"] = 8
print "z=",z
|
Puisque foo est appelé avec
3 en paramètre, cela affichera {'arg':
3, 'x': 1}. Cela ne devrait pas surprendre.
|
|
locals est une fonction qui retourne un dictionnaire et ici vous changez une valeur dans ce dictionnaire. Vous pourriez penser que
cela change la valeur de la
variable locale x à 2, mais
ce n’est pas le cas. locals ne retourne pas
vraiment l’espace de noms local mais une copie, modifier le
dictionnaire retourné ne change pas les variables de l’espace de
noms local.
|
|
Ceci affiche x= 1, pas x=
2.
|
|
Après avoir été déçu par locals, vous
pourriez penser que cela ne va pas changer la
valeur de z, mais ce serait une erreur. Pour
des raisons ayant trait à l’implémentation de
Python (dans le détail desquelles je ne
rentrerais pas, ne les comprenant pas totalement),
globals retourne l’espace de noms global
lui-même et non une copie, le comportement inverse de
locals. Donc, toute modification du
dictionnaire retourné par globals affecte les
variables globales.
|
|
Ceci affiche z= 8, pas z=
7.
|