Quelles exactement sont les règles de Python?
Si j'ai du code:
code1
class Foo:
code2
def spam.....
code3
for code4..:
code5
x()
Où se trouve x
? Certains choix possibles incluent la liste ci-dessous:
Il y a aussi le contexte pendant l'exécution, quand la fonction spam
est passée ailleurs. Et peut-être fonctions lambda passer un peu différemment?
Il doit y avoir une simple référence ou un algorithme quelque part. C'est un monde déroutant pour les programmeurs intermédiaires Python.
En fait, une règle concise pour Python Résolution de la portée, de Learning Python, 3rd. Ed. . (Ces règles sont spécifiques aux noms de variable et non aux attributs. Si vous la faites référence sans point, ces règles s'appliquent.)
Règle LEGB
L ocal - Noms attribués de quelque manière que ce soit dans une fonction (def
ou lambda
), et non déclarés globaux dans cette fonction
E nclosing-function - Noms attribués dans la portée locale de toute fonction englobant de manière statique (def
ou lambda
), de l'intérieur vers l'extérieur
G lobal (module) - Noms attribués au niveau supérieur d'un fichier de module, ou en exécutant une instruction global
dans un def
dans le fichier
B uilt-in (Python) - Noms prédéfinis dans le module de noms intégré: open
, range
, SyntaxError
, etc
Donc, dans le cas de
code1
class Foo:
code2
def spam():
code3
for code4:
code5
x()
La boucle for
ne possède pas son propre espace de noms. Dans l'ordre LEGB, les portées seraient
def spam
(dans code3
, code4
et code5
)def
)x
a été déclaré globalement dans le module (dans code1
)?x
en Python.x
ne sera jamais trouvé dans code2
(même dans les cas où on pourrait s'y attendre, voir réponse d'Antti ou ici ).
Essentiellement, la seule chose dans Python qui introduit une nouvelle portée est une définition de fonction. Les classes constituent un cas particulier en ce sens que tout ce qui est défini directement dans le corps est placé dans l'espace de nom de la classe, mais elles ne sont pas directement accessibles depuis les méthodes (ou les classes imbriquées) qu'elles contiennent.
Dans votre exemple, il n'y a que 3 portées dans lesquelles x sera recherché:
portée du spam - contenant tout ce qui est défini dans code3 et code5 (ainsi que code4, votre variable de boucle)
La portée globale - contenant tout ce qui est défini dans code1, ainsi que Foo (et tout changement ultérieur)
L'espace de noms intégré. Un cas particulier - il contient les diverses fonctions et types intégrés Python tels que len () et str (). En règle générale, aucun code d'utilisateur ne devrait le modifier. Attendez-vous donc à ce qu'il contienne les fonctions standard et rien d'autre.
D'autres étendues n'apparaissent que lorsque vous introduisez une fonction imbriquée (ou lambda) dans l'image. Celles-ci se comporteront à peu près comme vous le souhaiteriez cependant. La fonction imbriquée peut accéder à tout ce qui se trouve dans la portée locale, ainsi qu'à tout ce qui se trouve dans la portée de la fonction englobante. par exemple.
def foo():
x=4
def bar():
print x # Accesses x from foo's scope
bar() # Prints 4
x=5
bar() # Prints 5
Restrictions:
Il est possible d'accéder aux variables des portées autres que les variables de la fonction locale, mais il est impossible de rebondir vers de nouveaux paramètres sans autre syntaxe. Au lieu de cela, l’affectation créera une nouvelle variable locale au lieu d’affecter la variable dans la portée parente. Par exemple:
global_var1 = []
global_var2 = 1
def func():
# This is OK: It's just accessing, not rebinding
global_var1.append(4)
# This won't affect global_var2. Instead it creates a new variable
global_var2 = 2
local1 = 4
def embedded_func():
# Again, this doen't affect func's local1 variable. It creates a
# new local variable also called local1 instead.
local1 = 5
print local1
embedded_func() # Prints 5
print local1 # Prints 4
Afin de modifier réellement les liaisons de variables globales à partir de la portée d'une fonction, vous devez spécifier que la variable est globale avec le mot clé global. Par exemple:
global_var = 4
def change_global():
global global_var
global_var = global_var + 1
Actuellement, il n’existe aucun moyen de faire de même pour les variables entre et les étendues de fonctions , mais Python 3 introduit un nouveau mot-clé, "nonlocal
", qui agira de manière similaire à globale, mais pour les étendues de fonctions imbriquées.
Il n'y avait pas de réponse complète concernant le temps Python3, alors j'ai répondu ici.
Comme indiqué dans d'autres réponses, il existe 4 étendues de base, le LEGB, pour local, englobant, global et intégré. En plus de ceux-ci, il existe une portée spéciale, le corps de la classe, qui ne comprend pas de portée englobante pour les méthodes définies dans la classe; toute affectation dans le corps de classe fait que la variable à partir de là soit liée dans le corps de classe.
En particulier, no instruction de bloc, outre def
et class
, crée une étendue de variable. Dans Python 2, une compréhension de liste ne crée pas d'étendue de variable. Cependant, dans Python 3, la variable de boucle dans les interprétations 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 la fonction, vous pouvez réaffecter la variable au même nom dans le corps de la classe pour obtenir une variable de classe portant le même nom. d'autres recherches sur ce nom se résolvent à la variable de classe.
Une des plus grandes surprises pour de nombreux nouveaux venus dans Python est qu’une boucle for
ne crée pas de portée variable. Dans Python 2, les interprétations de liste ne créent pas non plus d'étendue (contrairement aux générateurs et interprétations de dict!!). À la place, elles fuient la valeur dans la fonction ou l'étendue 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 dans Python 2 - une expression lambda crée une étendue de variable, comme l’instruction def
serait, mais dans lambda aucune déclaration est autorisée. Une affectation étant une instruction dans Python signifie qu'aucune affectation de variable dans lambda n'est autorisée, mais une compréhension de liste est une expression ...
Ce problème a été corrigé dans Python 3 - aucune expression de compréhension ni aucun générateur ne laisse filtrer des variables.
Le global signifie vraiment la portée du module; le module principal python est le __main__
; tous les modules importés sont accessibles via la variable sys.modules
; pour avoir accès à __main__
on 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 affecté à la portée actuelle (sauf dans la portée de la classe), il sera considéré comme appartenant à cette portée, sinon, il sera considéré comme appartenant à toute portée englobante affectant la variable (elle ne sera peut-être pas attribuée). encore, ou pas du tout), ou enfin la portée globale. Si la variable est considérée comme locale mais qu'elle n'a pas encore été définie ou qu'elle a été supprimée, la lecture de la valeur de la variable aura pour résultat 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 veut 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
Ceci est également possible même s'il était 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
Dans python 2, il n'existe pas de moyen facile de modifier la valeur dans la portée englobante. cela est généralement simulé en ayant une valeur modifiable, 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, la 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.
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 alors recherché à partir du module builtins; le nom du module a été changé de python 2 à python 3; dans python 2, il s'agissait de __builtin__
et dans python 3, il s'appelle maintenant builtins
. Si vous attribuez un attribut de module intégré, il sera visible par la suite de tout module sous forme de variable globale lisible, à moins que ce module ne l'ombre de 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'instruction print
. Dans Python 2.6-2.7, vous pouvez obtenir la fonction Python 3 print
avec:
import __builtin__
print3 = __builtin__.__dict__['print']
Le from __future__ import print_function
n'importe pas la fonction print
dans Python 2 - il désactive simplement les règles d'analyse de l'instruction print
dans le module actuel, en gérant print
comme tout autre identifiant de variable, permettant ainsi à la fonction print
d'être recherchée dans les fonctions intégrées.
Les règles de cadrage pour Python 2.x ont déjà été décrites dans d'autres réponses. La seule chose que je voudrais ajouter est que, dans Python 3.0, il existe également le concept de portée non locale (indiqué par le mot clé 'nonlocal'). Cela vous permet d’accéder directement aux portées externes et vous offre la possibilité de réaliser quelques astuces, y compris des fermetures lexicales (sans hacks laids impliquant des objets mutables).
EDIT: Voici le PEP avec plus d'informations à ce sujet.
Un exemple un peu plus complet de la portée:
from __future__ import print_function # for python 2 support
x = 100
print("1. Global x:", x)
class Test(object):
y = x
print("2. Enclosed y:", y)
x = x + 1
print("3. Enclosed x:", x)
def method(self):
print("4. Enclosed self.x", self.x)
print("5. Global x", x)
try:
print(y)
except NameError as e:
print("6.", e)
def method_local_ref(self):
try:
print(x)
except UnboundLocalError as e:
print("7.", e)
x = 200 # causing 7 because has same name
print("8. Local x", x)
inst = Test()
inst.method()
inst.method_local_ref()
sortie:
1. Global x: 100
2. Enclosed y: 100
3. Enclosed x: 101
4. Enclosed self.x 101
5. Global x 100
6. global name 'y' is not defined
7. local variable 'x' referenced before assignment
8. Local x 200
Python résout vos variables avec - en général - trois espaces de noms disponibles.
À tout moment de l'exécution, il existe au moins trois portées imbriquées dont les espaces de noms sont directement accessibles: la portée la plus à l'intérieur, qui fait l'objet de la recherche en premier, contient les noms locaux; les espaces de noms de toutes les fonctions englobantes, dans lesquelles la recherche commence en commençant par la portée englobante la plus proche; la portée du milieu, recherchée ensuite, contient les noms globaux du module actuel; et la portée la plus externe (recherchée en dernier) est l'espace de noms contenant les noms intégrés.
Il y a deux fonctions: globals
et locals
qui vous montrent le contenu de deux de ces espaces de noms.
Les espaces de noms sont créés par les packages, les modules, les classes, la construction des objets et les fonctions. Il n'y a pas d'autres types d'espaces de noms.
Dans ce cas, l'appel d'une fonction nommée x
doit être résolu dans l'espace de noms local ou dans l'espace de noms global.
Local dans ce cas, est le corps de la méthode function Foo.spam
.
Global est - bien - global.
La règle consiste à rechercher les espaces locaux imbriqués créés par les fonctions de méthode (et les définitions de fonctions imbriquées), puis à effectuer une recherche globale. C'est ça.
Il n'y a pas d'autres portées. L'instruction for
(et d'autres instructions composées telles que if
et try
) ne crée pas de nouvelles étendues imbriquées. Seules les définitions (packages, modules, fonctions, classes et instances d'objet).
Dans une définition de classe, les noms font partie de l'espace de noms de la classe. code2
, par exemple, doit être qualifié par le nom de la classe. Généralement Foo.code2
. Cependant, self.code2
fonctionnera également, car les objets Python considèrent la classe qui le contient comme une solution de secours.
Un objet (une instance d'une classe) a des variables d'instance. Ces noms sont dans l'espace de noms de l'objet. Ils doivent être qualifiés par l'objet. (variable.instance
.)
Dans une méthode de classe, vous avez des variables locales et globales. Vous dites self.variable
pour choisir l'instance comme espace de nom. Vous remarquerez que self
est un argument de chaque fonction membre de la classe, ce qui en fait une partie de l'espace de noms local.
Voir Règles d'étendue Python , Étendue Python , Étendue variable .
Où se trouve x?
x n'est pas trouvé car vous ne l'avez pas défini. :-) On pourrait le trouver dans code1 (global) ou code3 (local) si vous le mettez là.
code2 (membres de la classe) n'est pas visible dans les méthodes de la même classe - vous y accéderiez généralement en utilisant self. code4/code5 (boucles) ont la même portée que code3, donc si vous écriviez dans x, vous modifieriez l'instance x définie dans code3 sans créer de nouveau x.
Python a une portée statique. Par conséquent, si vous transmettez le "spam" à une autre fonction, le spam aura toujours accès aux éléments globaux du module dont il est issu (défini dans code1), ainsi qu’à toute autre portée contenant (voir ci-dessous). Les membres de code2 seraient à nouveau accessibles via self.
lambda n'est pas différent de def. Si vous utilisez un lambda dans une fonction, cela revient à définir une fonction imbriquée. À partir de Python 2.2, des étendues imbriquées sont disponibles. Dans ce cas, vous pouvez lier x à n’importe quel niveau d’imbrication de fonctions et Python prendra l’instance la plus interne:
x= 0
def fun1():
x= 1
def fun2():
x= 2
def fun3():
return x
return fun3()
return fun2()
print fun1(), x
2 0
fun3 voit l'instance x de la portée contenant la plus proche, qui est la portée de la fonction associée à fun2. Mais les autres instances x, définies dans fun1 et globalement, ne sont pas affectées.
Before nested_scopes - in Python pre-2.1, et 2.1 sauf si vous demandez spécifiquement la fonctionnalité à l'aide d'une importation de future- - les étendues fun1 et fun2 ne sont pas visibles pour fun3, donc la réponse de S.Lott est maintenue et vous obtiendrez le x global:
0 0
En python,
toute variable à laquelle une valeur est attribuée est locale au bloc dans lequel l'affectation apparaît.
Si une variable ne peut pas être trouvée dans la portée actuelle, veuillez vous référer à la commande LEGB.