Question

Quelles sont exactement les règles de portée Python?

Si j'ai du code:

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

Où trouve-t-on x ? Certains choix possibles incluent la liste ci-dessous:

  1. Dans le fichier source inclus
  2. Dans l'espace de noms de la classe
  3. Dans la définition de la fonction
  4. Dans la variable d'index de la boucle for
  5. Dans la boucle for

Il existe également un contexte lors de l'exécution, lorsque la fonction spam est transmise ailleurs. Et peut-être que les fonctions lambda sont un peu différentes?

Il doit exister une simple référence ou un algorithme quelque part. C’est un monde déroutant pour les programmeurs Python de niveau intermédiaire.

Était-ce utile?

La solution

En fait, règle concise pour la résolution de l'étendue Python, tirée de Learning Python, 3ème. 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 , Local & # 8212; Noms attribués d'une manière quelconque dans une fonction ( def ou lambda )), et non déclarés globaux dans cette fonction.

E , Paramètres locaux de la fonction d’inclinaison & # 8212; Nom 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 , Global (module) & # 8212; Noms attribués au niveau supérieur du fichier de module ou en exécutant une instruction global dans un def du fichier.

B , Intégré (Python) & # 8212; Noms prédéfinis dans le module de noms intégré: open , plage , SyntaxError , ...

Donc, dans le cas de

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

La boucle for n'a pas son propre espace de noms. Dans l'ordre LEGB, les portées seraient

L: local, dans spam (dans code3 , code 4 , code5 ).

E: Fonction incluse, toute fonction englobante (si l'exemple entier était dans un autre def )

G: Global. Existe-t-il des x déclarés globalement dans le module ( code1 )?

B: Tout élément x intégré à Python.

x ne sera jamais trouvé dans code2 (même dans les cas où vous vous en attendez, consultez Réponse d'Antti ou ici ).

Autres conseils

En gros, la seule chose en Python qui introduit une nouvelle portée est une définition de fonction. Les classes constituent un cas particulier dans la mesure où 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é:

  • la 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 différentes fonctions et types intégrés de 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 souhaitiez 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:

Les variables des portées autres que les variables de la fonction locale sont accessibles, mais vous ne pouvez pas utiliser les nouveaux paramètres sans nouvelle 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

Pour 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

Il n’existe actuellement aucun moyen de procéder de la même manière pour les variables incluses dans les étendues de fonctions , mais Python 3 introduit un nouveau mot clé, "& <;> code> non local " qui agira de manière similaire à globale, mais pour les portées de fonctions imbriquées.

Il n’ya pas eu de réponse complète concernant l’heure Python3, j’ai donc donné une réponse ici.

Comme indiqué dans d'autres réponses, il existe 4 étendues de base, le LEGB, pour local, englobant, global et intégré. Outre ces éléments, il existe une étendue spéciale, le corps de la classe , qui ne comprend pas d'étendue englobante pour les méthodes définies dans la classe. toute affectation dans le corps de la classe fait en sorte que la variable soit liée dans le corps de la classe.

En particulier, aucune instruction de bloc, outre def et classe , créez une étendue de variable. En Python 2, la compréhension de liste ne crée pas de portée variable, mais en Python 3, la variable de boucle dans la compréhension de liste est créée dans une nouvelle portée.

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 du même nom; recherches supplémentaires sur cette résolution de noms à la variable de classe à la place.

Une des surprises majeures pour de nombreux nouveaux venus dans Python est qu’une boucle pour ne crée pas de portée variable. En Python 2, les interprétations de liste ne créent pas non plus d’objet (contrairement aux interpréteurs de dicteurs et de dicteurs!). 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 sournois (ou affreux si vous voulez) de créer des variables modifiables dans des expressions lambda dans Python 2. Une expression lambda crée une portée de variable, comme l’instruction def . serait, mais dans lambda aucune déclaration est autorisée. L’affectation étant une instruction en Python, cela signifie qu’aucune affectation de variable dans lambda n’est autorisée, mais une liste de compréhension est une expression ...

Ce problème a été corrigé dans Python 3: pas d’expression de compréhension ni de générateur de variables de fuite.

Le global signifie vraiment la portée du module; le module principal de python est le __ main __ ; tous les modules importés sont accessibles via la variable sys.modules ; pour accéder à __ main __ , vous pouvez utiliser sys.modules ['__ main __'] ou importer __main __ ; il est parfaitement acceptable d'y accéder et d'y attribuer des attributs; ils apparaîtront sous forme de 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 (il pas encore, ou pas du tout), ou enfin la portée globale. Si la variable est considérée comme locale, mais qu'elle n'est pas encore définie ou qu'elle a été supprimée, la lecture de la valeur de la variable entraînera 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()

L'étendue peut déclarer qu'elle veut explicitement modifier la variable globale (étendue 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

En python 2, il n’existe aucun moyen simple 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, 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.

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; en python 2, il s'agissait de __ built__ __ et en 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 la forme d'une variable globale lisible, à moins que ce module ne les masque

Les règles de cadrage de 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 d'une étendue non locale (indiqué par le mot clé 'nonlocal'). Cela vous permet d’accéder directement aux portées externes et vous donne la possibilité de réaliser quelques astuces, y compris des fermetures lexicales (sans hacks laids impliquant des objets mutables).

EDIT: voici le PEP contenant 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 - généralement - trois espaces de noms disponibles.

  

À tout moment de l'exécution, il y a   sont au moins trois portées imbriquées dont   Les espaces de noms sont directement accessibles:   la portée la plus interne, qui est recherchée   en premier lieu, contient les noms locaux; le   espaces de noms de toutes les fonctions englobantes,   qui sont recherchés en commençant par le   portée englobante la plus proche; le milieu   scope, recherché ensuite, contient le   les noms globaux du module actuel; et le   la portée la plus externe (la dernière recherchée) est la   espace de noms contenant les noms intégrés.

Il existe deux fonctions: globals et locals qui affichent 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 fonction method 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 leurs définitions de fonctions), 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 si et try ) ne crée pas de nouvelles portées 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 nom 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 noms. Vous remarquerez que self est un argument pour chaque fonction membre de la classe, ce qui en fait une partie de l'espace de noms local.

Voir Règles de portée de Python , ÉtenduePython , Portée variable .

  

Où trouve-t-on 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 pour le code dans les méthodes de la même classe. Vous y accéderiez habituellement à l'aide de self. code4 / code5 (boucles) a la même portée que code3, donc si vous écriviez dans x, vous modifieriez l'instance de x définie dans code3 sans créer de nouveau x.

Python est statiquement limité, donc si vous transmettez le "spam" & # 8216; spam & # 8217; Pour une autre fonction, le spam aura toujours accès aux éléments globaux du module dont il est issu (défini dans code1), ainsi les membres de code2 seraient à nouveau accessibles par le biais de leur compte personnel.

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 choisira 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 x instances, définies dans fun1 et globalement, ne sont pas affectées.

Before nested_scopes & # 8201; & # 8212; & # 8201; en Python antérieurs à 2.1, et en 2.1 à moins que vous ne demandiez spécifiquement la fonctionnalité utilisant une importation future de # 8201; & # 8212; & # 8201; Les champs d'application de fun1 et fun2 ne sont pas visibles pour fun3. La réponse de S.Lott est donc valable et vous obtiendrez le x global:

0 0

En Python,

  

toute variable affectée à une valeur est locale au bloc dans lequel   l'affectation apparaît.

Si une variable est introuvable dans l'étendue actuelle, veuillez vous référer à l'ordre du LEGB.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top