Il n'y avait pas de réponse complète concernant le temps Python3, j'ai donc fait une réponse ici. La plupart de ce qui est décrit ici est détaillé dans la résolution 4.2.2 des noms de la documentation Python 3.
Comme indiqué dans d'autres réponses, il existe 4 étendues de base, le LEGB, pour Local, Enclosing, Global et Builtin. En plus de ceux-ci, il existe une portée spéciale, le corps de classe , qui ne comprend pas de portée englobante pour les méthodes définies dans la classe; toutes les affectations au sein du corps de classe rendent la variable à partir de là liée dans le corps de classe.
Surtout, aucune instruction de bloc, outre def
et class
, crée une portée variable. Dans Python 2, une compréhension de liste ne crée pas une étendue de variable, cependant dans Python 3 la variable de boucle dans les compréhensions de liste est créée dans une nouvelle étendue.
Démontrer les particularités du corps de classe
x = 0
class X(object):
y = x
x = x + 1 # x is now a variable
z = x
def method(self):
print(self.x) # -> 1
print(x) # -> 0, the global x
print(y) # -> NameError: global name 'y' is not defined
inst = X()
print(inst.x, inst.y, inst.z, x) # -> (1, 0, 1, 0)
Ainsi, contrairement au corps de fonction, vous pouvez réaffecter la variable au même nom dans le corps de classe, pour obtenir une variable de classe avec le même nom; d'autres recherches sur ce nom se résolvent à la place à la variable de classe.
L'une des plus grandes surprises pour de nombreux nouveaux arrivants en Python est qu'une for
boucle ne crée pas une portée variable. Dans Python 2, les listes de compréhension ne créent pas non plus de portée (contrairement aux générateurs et aux interprétations de dict!). Au lieu de cela, elles fuient la valeur dans la fonction ou la portée globale:
>>> [ i for i in range(5) ]
>>> i
4
Les compréhensions peuvent être utilisées comme un moyen astucieux (ou affreux si vous voulez) de créer des variables modifiables dans les expressions lambda en Python 2 - une expression lambda crée une portée variable, comme le def
ferait l' instruction, mais dans lambda aucune instruction n'est autorisée. L'affectation étant une instruction en Python signifie qu'aucune affectation de variable dans lambda n'est autorisée, mais une compréhension de liste est une expression ...
Ce comportement a été corrigé dans Python 3 - aucune expression de compréhension ni aucune variable de fuite de générateur.
Le global signifie vraiment la portée du module; le module python principal est le __main__
; tous les modules importés sont accessibles via la sys.modules
variable; pour accéder à l' __main__
un peut utiliser sys.modules['__main__']
, ou import __main__
; il est parfaitement acceptable d'y accéder et d'y attribuer des attributs; ils apparaîtront comme des variables dans la portée globale du module principal.
Si un nom est jamais attribué à dans la portée actuelle (sauf dans la portée de classe), il sera considéré comme appartenant à cette portée, sinon il sera considéré comme appartenant à toute portée englobante qui affecte à la variable (il pourrait ne pas être affecté encore, ou pas du tout), ou enfin la portée mondiale. Si la variable est considérée comme locale, mais qu'elle n'est pas encore définie ou a été supprimée, la lecture de la valeur de la variable se traduira par UnboundLocalError
, qui est une sous-classe de NameError
.
x = 5
def foobar():
print(x) # causes UnboundLocalError!
x += 1 # because assignment here makes x a local variable within the function
# call the function
foobar()
La portée peut déclarer qu'elle souhaite explicitement modifier la variable globale (portée du module), avec le mot clé global:
x = 5
def foobar():
global x
print(x)
x += 1
foobar() # -> 5
print(x) # -> 6
Cela est également possible même s'il a été masqué dans la portée englobante:
x = 5
y = 13
def make_closure():
x = 42
y = 911
def func():
global x # sees the global value
print(x, y)
x += 1
return func
func = make_closure()
func() # -> 5 911
print(x, y) # -> 6 13
En python 2, il n'y a pas de moyen facile de modifier la valeur dans la portée englobante; généralement, cela est simulé en ayant une valeur mutable, telle qu'une liste de longueur 1:
def make_closure():
value = [0]
def get_next_value():
value[0] += 1
return value[0]
return get_next_value
get_next = make_closure()
print(get_next()) # -> 1
print(get_next()) # -> 2
Cependant en python 3, le nonlocal
vient à la rescousse:
def make_closure():
value = 0
def get_next_value():
nonlocal value
value += 1
return value
return get_next_value
get_next = make_closure() # identical behavior to the previous example.
La nonlocal
documentation indique que
Les noms répertoriés dans une instruction non locale, contrairement à ceux répertoriés dans une instruction globale, doivent faire référence à des liaisons préexistantes dans une étendue englobante (l'étendue dans laquelle une nouvelle liaison doit être créée ne peut pas être déterminée sans ambiguïté).
c'est-à-dire nonlocal
fait toujours référence à la portée externe non globale la plus interne où le nom a été lié (c'est-à-dire affecté à, y compris utilisé comme for
variable cible, dans la with
clause ou comme paramètre de fonction).
Toute variable qui n'est pas considérée comme étant locale à la portée actuelle, ou toute portée englobante, est une variable globale. Un nom global est recherché dans le dictionnaire global du module; s'il n'est pas trouvé, le global est ensuite recherché depuis le module intégré; le nom du module a été changé de python 2 à python 3; en python 2 c'était __builtin__
et en python 3 il s'appelle maintenant builtins
. Si vous attribuez à un attribut du module intégré, il sera visible par la suite à n'importe quel module en tant que variable globale lisible, à moins que ce module ne les masque avec sa propre variable globale du même nom.
La lecture du module intégré peut également être utile; supposons que vous souhaitiez la fonction d'impression de style python 3 dans certaines parties du fichier, mais que d'autres parties du fichier utilisent toujours l' print
instruction. Dans Python 2.6-2.7, vous pouvez obtenir la print
fonction Python 3 avec:
import __builtin__
print3 = __builtin__.__dict__['print']
En from __future__ import print_function
fait, la print
fonction n'est pas importée n'importe où dans Python 2 - au lieu de cela, elle désactive simplement les règles d'analyse pour l' print
instruction dans le module actuel, se manipulant print
comme tout autre identificateur de variable, et permettant ainsi à print
la fonction d'être recherchée dans les commandes internes.