Quelles sont exactement les règles de délimitation de Python?
Si j'ai du code:
code1
class Foo:
code2
def spam.....
code3
for code4..:
code5
x()
Où se x
trouve? Certains choix possibles incluent la liste ci-dessous:
- Dans le fichier source joint
- Dans l'espace de noms de classe
- Dans la définition de fonction
- Dans la variable d'index de boucle for
- À l'intérieur de la boucle for
Il y a aussi le contexte pendant l'exécution, lorsque la fonction spam
est passée ailleurs. Et peut-être que les fonctions lambda passent 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 Python intermédiaires.
python
scope
dynamic-languages
Charles Merriam
la source
la source
Réponses:
En fait, une règle concise pour la résolution de l'étendue de Python, tirée de Learning Python, 3rd. Ed. . (Ces règles sont spécifiques aux noms de variables et non aux attributs. Si vous la référencez 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
oulambda
), et non déclarés globaux dans cette fonctionE nclosing-function - Noms attribués dans la portée locale de toutes les fonctions (
def
oulambda
) enfermant statiquement , de l'intérieur vers l'extérieurG lobal (module) - Noms attribués au niveau supérieur d'un fichier de module, ou en exécutant une
global
instruction dans undef
fichierB onstruit en (Python) - Les noms réservés dans le module noms intégrés:
open
,range
,SyntaxError
, etc.Donc, dans le cas de
La
for
boucle n'a pas son propre espace de noms. Dans l'ordre LEGB, les portées seraientdef spam
(encode3
,code4
etcode5
)def
)x
déclaré globalement dans le module (encode1
)?x
en Python.x
ne sera jamais trouvé danscode2
(même dans les cas où vous pourriez vous y attendre, voir la réponse d'Antti ou ici ).la source
global(var_name)
est syntaxiquement incorrect. La syntaxe correcte seraitglobal var_name
sans parenthèses. Vous avez cependant un argument valable.>>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3
y
est écrit et qu'il n'y a pas deglobal y
déclarations - voir le commentaire de @ Peter.Essentiellement, la seule chose en Python qui introduit une nouvelle portée est une définition de fonction. Les classes sont un peu un cas particulier dans la mesure où tout ce qui est défini directement dans le corps est placé dans l'espace de noms de la classe, mais elles ne sont pas directement accessibles depuis les méthodes (ou classes imbriquées) qu'elles contiennent.
Dans votre exemple, il n'y a que 3 étendues 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 le code1, ainsi que Foo (et tout ce qui change après)
L'espace de noms intégré. Un petit cas particulier - il contient les différentes fonctions et types intégrés de Python tels que len () et str (). En général, cela ne devrait être modifié par aucun code utilisateur, alors attendez-vous à 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. Ceux-ci se comporteront à peu près comme vous vous attendez cependant. La fonction imbriquée peut accéder à tout dans la portée locale, ainsi qu'à tout ce qui se trouve dans la portée de la fonction englobante. par exemple.
Restrictions:
Les variables dans des étendues autres que les variables de la fonction locale sont accessibles, mais ne peuvent pas être renvoyées à de nouveaux paramètres sans syntaxe supplémentaire. Au lieu de cela, l'affectation créera une nouvelle variable locale au lieu d'affecter la variable dans la portée parent. Par exemple:
Afin de modifier réellement les liaisons des variables globales à partir d'une portée de fonction, vous devez spécifier que la variable est globale avec le mot-clé global. Par exemple:
Actuellement, il n'existe aucun moyen de faire de même pour les variables dans les étendues de fonctions englobantes , mais Python 3 introduit un nouveau mot clé, "
nonlocal
" qui agira de la même manière que global, mais pour les étendues de fonctions imbriquées.la source
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
etclass
, 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
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: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 lasys.modules
variable; pour accéder à l'__main__
un peut utilisersys.modules['__main__']
, ouimport __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 deNameError
.La portée peut déclarer qu'elle souhaite explicitement modifier la variable globale (portée du module), avec le mot clé global:
Cela est également possible même s'il a été masqué dans la portée englobante:
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:
Cependant en python 3, le
nonlocal
vient à la rescousse:La
nonlocal
documentation indique quec'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é commefor
variable cible, dans lawith
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 maintenantbuiltins
. 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 laprint
fonction Python 3 avec:En
from __future__ import print_function
fait, laprint
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 manipulantprint
comme tout autre identificateur de variable, et permettant ainsi àprint
la fonction d'être recherchée dans les commandes internes.la source
Les règles de portée pour Python 2.x ont déjà été décrites dans d'autres réponses. La seule chose que j'ajouterais est qu'en Python 3.0, il y a aussi le concept d'une portée non locale (indiquée par le mot-clé 'nonlocal'). Cela vous permet d'accéder directement aux étendues externes et ouvre la possibilité de faire quelques astuces soignées, y compris des fermetures lexicales (sans hacks laids impliquant des objets mutables).
EDIT: Voici le PEP avec plus d'informations à ce sujet.
la source
Un exemple de portée un peu plus complet:
production:
la source
method
etmethod_local_ref
doivent être soulignées.method
peut accéder à la variable globale et l'imprimer comme dans5. Global x
. Maismethod_local_ref
ne peut pas car plus tard, il définit une variable locale avec ce même nom. Vous pouvez tester cela en supprimant lax = 200
ligne et voir la différencePython résout vos variables avec - généralement - trois espaces de noms disponibles.
Il y a deux fonctions:
globals
etlocals
qui vous montrent le contenu de deux de ces espaces de noms.Les espaces de noms sont créés par des packages, des modules, des classes, la construction d'objets et des fonctions. Il n'y a pas d'autres saveurs d'espaces de noms.
Dans ce cas, l'appel à une fonction nommée
x
doit être résolu dans l'espace de noms local ou l'espace de noms global.Local dans ce cas, est le corps de la fonction méthode
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'autre portée. L'
for
instruction (et d'autres instructions composées commeif
ettry
) ne crée pas de nouvelles étendues imbriquées. Uniquement les définitions (packages, modules, fonctions, classes et instances d'objets.)Dans une définition de classe, les noms font partie de l'espace de noms de classe.
code2
, par exemple, doit être qualifié par le nom de la classe. GénéralementFoo.code2
. Cependant,self.code2
cela fonctionnera également parce que les objets Python regardent la classe contenante comme solution de secours.Un objet (une instance d'une classe) a des variables d'instance. Ces noms se trouvent dans l'espace de noms de l'objet. Ils doivent être qualifiés par l'objet. (
variable.instance
.)À partir d'une méthode de classe, vous avez des sections locales et globales. Vous dites
self.variable
de choisir l'instance comme espace de noms. Vous remarquerez qu'ilself
s'agit d'un argument pour chaque fonction membre de la classe, ce qui en fait une partie de l'espace de noms local.Voir Python Règles Scope , Champ d' application Python , la portée des variables .
la source
Python has two namespaces available. Global and local-to-something.
x n'est pas trouvé car vous ne l'avez pas défini. :-) Il pourrait être trouvé dans code1 (global) ou code3 (local) si vous le mettez là.
code2 (membres de la classe) ne sont pas visibles pour coder à l'intérieur des méthodes de la même classe - vous y accéderez généralement en utilisant self code4 / code5 (boucles) vivent dans la même portée que code3, donc si vous écriviez à x là-dedans, vous changeriez l'instance x définie dans code3, pas un nouveau x.
Python a une portée statique, donc si vous passez du `` spam '' à une autre fonction, le spam aura toujours accès aux globaux du module dont il est issu (défini dans le code1), et à tout autre périmètre contenant (voir ci-dessous). Les membres de code2 seraient à nouveau accessibles via self.
lambda n'est pas différent de def. Si vous avez un lambda utilisé dans une fonction, cela revient à définir une fonction imbriquée. Dans Python 2.2 et versions ultérieures, les étendues imbriquées sont disponibles. Dans ce cas, vous pouvez lier x à n'importe quel niveau d'imbrication de fonction et Python récupérera l'instance la plus interne:
fun3 voit l'instance x de la portée contenante 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.
Avant nested_scopes - en Python pré-2.1 et en 2.1, sauf si vous demandez spécifiquement la fonctionnalité à l'aide d'une importation future - les étendues de fun1 et fun2 ne sont pas visibles pour fun3, donc la réponse de S.Lott est valable et vous obtiendrez le x global :
la source
En Python,
Si une variable est introuvable dans la portée actuelle, veuillez vous référer à la commande LEGB.
la source