492 votes

Brève description des règles de scoping ?

Quoi exactement sont les règles de cadrage de Python ?

Si j'ai du code :

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

Où se trouve x trouvé ? Parmi les choix possibles, on trouve la liste ci-dessous :

  1. Dans le fichier source ci-joint
  2. Dans l'espace de nom de la classe
  3. Dans la définition de la fonction
  4. Dans la boucle for, la variable index
  5. A l'intérieur de la boucle for

Il y a aussi le contexte pendant l'exécution, lorsque la fonction spam est transmis ailleurs. Et peut-être fonctions lambda passer un peu différemment ?

Il doit y avoir une référence ou un algorithme simple quelque part. C'est un monde confus pour les programmeurs Python intermédiaires.

2 votes

Les règles de cadrage sont décrites de manière assez succincte - mais aussi complète - dans la documentation Python : docs.python.org/3/reference/ .

433voto

Rizwan Kassim Points 2467

En fait, une règle concise pour la résolution de Python Scope, de Learning Python, 3rd. Ed. . (Ces règles sont spécifiques aux noms de variables, et non aux attributs. Si vous y 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 à l'intérieur d'une fonction ( def o lambda ), et non déclarée globale dans cette fonction

  • E nclosing-function - Noms assignés dans la portée locale de toutes les fonctions statiquement englobantes ( def o 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 par l'exécution de la commande global dans un def dans le fichier

  • B uilt-in (Python) - Noms préassignés dans le module des noms intégrés : open , range , SyntaxError etc.

Ainsi, dans le cas de

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

En for loop n'a pas son propre espace de nom. Dans l'ordre LEGB, les scopes seraient les suivants

  • L : Local en def spam (en code3 , code4 et code5 )
  • E : Toute fonction englobante (si l'exemple entier était dans une autre def )
  • G : Y avait-il des x déclaré globalement dans le module (dans code1 ) ?
  • B : Toute fonction intégrée x en Python.

x ne sera jamais trouvé dans code2 (même dans les cas où l'on pourrait s'y attendre, voir Réponse d'Antti o aquí ).

47 votes

Une mise en garde concernant l'accès global : la lecture d'une variable globale peut se faire sans déclaration explicite, mais l'écriture dans cette variable sans déclarer global(var_name) créera une nouvelle instance locale.

12 votes

En fait, @Peter, global(var_name) est syntaxiquement incorrect. La syntaxe correcte serait global var_name sans parenthèses. Mais vous avez un point valable.

0 votes

Si c'est le cas, alors pourquoi la variable "y" de foo n'est pas visible par "bar" en dessous : >>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3

158voto

Brian Points 48423

Essentiellement, la seule chose en Python qui introduit une nouvelle portée est une définition de fonction. Les classes sont un cas un peu 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 trois champs d'application dans lesquels x sera recherché :

  • la portée du spam - contenant tout ce qui est défini dans le code3 et le code5 (ainsi que le code4, votre variable de boucle)

  • La portée globale - contenant tout ce qui est défini dans code1, ainsi que Foo (et tout ce qui est modifié après lui)

  • L'espace de noms builtins. C'est un cas un peu particulier - il contient les diverses fonctions et types intégrés de Python tels que len() et str(). En général, cet espace ne devrait pas être modifié par le code utilisateur, donc attendez-vous à ce qu'il contienne les fonctions standard et rien d'autre.

Des champs d'application supplémentaires n'apparaissent que lorsque vous introduisez une fonction imbriquée (ou lambda) dans le tableau. Ces fonctions se comporteront comme vous vous y attendez. 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. ex.

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 à des variables situées dans des champs d'application autres que ceux des variables de la fonction locale, mais elles ne peuvent pas être rebasculées en nouveaux paramètres sans autre syntaxe. Au lieu de cela, l'affectation créera un nouveau local au lieu d'affecter la variable dans la portée parent. 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 pouvoir modifier les liaisons des variables globales à l'intérieur de la portée d'une fonction, vous devez spécifier que la variable est globale avec le mot-clé global. Ex :

global_var = 4
def change_global():
    global global_var
    global_var = global_var + 1

Actuellement, il n'y a pas de moyen de faire la même chose pour les variables dans l'entourage. fonction mais Python 3 introduit un nouveau mot-clé, " nonlocal "qui agira de la même manière que global, mais pour les fonctions imbriquées.

113voto

Antti Haapala Points 11542

Il n'y avait pas de réponse approfondie concernant le temps Python3, donc j'ai fait une réponse ici. La plupart de ce qui est décrit ici est détaillé dans le document 4.2.2 Résolution des noms de la documentation de Python 3.

Comme indiqué dans d'autres réponses, il existe 4 champs d'application de base, les LEGB, pour Local, Enclosing, Global et Builtin. En plus de celles-ci, il y a une portée spéciale, le corps de classe qui ne comporte pas de portée englobante pour les méthodes définies dans la classe ; toute affectation dans le corps de la classe fait que la variable est désormais liée dans le corps de la classe.

Surtout, pas de l'énoncé du bloc, en plus def y class créer une variable de portée. Dans Python 2, une compréhension de liste ne crée pas de portée de variable, cependant dans Python 3, la variable de boucle dans les compréhensions de liste est créée dans une nouvelle portée.

Démontrer les particularités du corps de la 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 à ce qui se passe dans le corps de la fonction, vous pouvez réassigner la variable au même nom dans le corps de la classe, pour obtenir une variable de classe avec le même nom ; les recherches ultérieures sur ce nom résolvent vers la variable de classe.


L'une des plus grandes surprises pour de nombreux nouveaux venus dans Python est qu'un fichier de type for ne crée pas de portée de variable. En Python 2, les compréhensions de listes ne créent pas non plus de portée (alors que les générateurs et les compréhensions de dicts le font !) Au lieu de cela, ils font fuir 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 horrible si vous voulez) de rendre les variables modifiables dans les expressions lambda en Python 2 - une expression lambda crée une portée de variable, comme l'expression def mais dans les lambdas, aucune déclaration n'est autorisée. L'assignation étant une instruction en Python, aucune assignation de variable n'est autorisée dans lambda, mais une compréhension de liste est une expression...

Ce comportement a été corrigé dans Python 3 - les expressions de compréhension ou les générateurs ne fuient plus les variables.


Le global signifie en réalité la portée du module ; le module python principal est le module __main__ ; tous les modules importés sont accessibles par l'intermédiaire du sys.modules variable ; pour avoir accès à __main__ on peut utiliser sys.modules['__main__'] ou import __main__ il est parfaitement acceptable d'accéder et d'assigner des attributs à cet endroit ; ils apparaîtront comme des variables dans la portée globale du module principal.


Si un nom est assigné dans la portée courante (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 qui assigne à la variable (elle peut ne pas être encore assignée, 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 assignée, ou qu'elle a été supprimée, la lecture de la valeur de la variable donnera le résultat suivant 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

Cela est également possible même si elle a été occultée 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'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 avec une longueur de 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, dans python 3, l'option 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.

En nonlocal documentation dit que

Les noms énumérés dans une déclaration non locale, contrairement à ceux énumérés dans une déclaration globale, doivent faire référence à des liaisons préexistantes dans une portée englobante (la portée dans laquelle une nouvelle liaison doit être créée ne peut être déterminée sans ambiguïté).

c'est-à-dire nonlocal fait toujours référence à l'étendue non globale extérieure la plus proche dans laquelle le nom a été lié (c'est-à-dire assigné, y compris utilisé en tant qu'attribut de l'attribut for variable cible, dans le with ou en tant que 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. Le nom d'une variable globale est recherché dans le dictionnaire global du module ; s'il n'est pas trouvé, la variable globale est alors recherchée dans le module builtins ; le nom du module a été modifié de python 2 à python 3 ; dans python 2, il s'agissait de __builtin__ et dans python 3, il est maintenant appelé builtins . Si vous assignez à un attribut du module builtins, il sera visible par la suite à n'importe quel module comme une variable globale lisible, à moins que ce module ne l'ombrage avec sa propre variable globale avec le même nom.


La lecture du module intégré peut également s'avérer utile ; supposons que vous souhaitiez utiliser la fonction print de python 3 dans certaines parties du fichier, mais que d'autres parties du fichier utilisent toujours la fonction print de python 3. print déclaration. Dans Python 2.6-2.7, vous pouvez vous procurer la version Python 3 print fonction avec :

import __builtin__

print3 = __builtin__.__dict__['print']

En from __future__ import print_function n'importe pas réellement le print dans Python 2 - au lieu de cela, elle désactive simplement les règles d'analyse syntaxique pour la fonction print dans le module actuel, en traitant print comme n'importe quel autre identifiant de variable, et permettant ainsi à la print la fonction soit recherchée dans les builtins.

0 votes

Heureux de voir enfin une réponse qui mentionne le spécial. corps de classe qui n'est pas couverte par la règle bien connue du LEGB.

23voto

Jeremy Cantrell Points 7858

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 y a aussi le concept de portée non-locale (indiqué par le mot-clé 'nonlocal'). Cela vous permet d'accéder directement à des scopes externes, et vous donne la possibilité de faire quelques trucs intéressants, y compris des fermetures lexicales (sans les bidouillages affreux impliquant des objets mutables).

EDIT : Voici le PEP avec plus d'informations à ce sujet.

13voto

S.Lott Points 207588

Python résout vos variables avec -- généralement -- trois espaces de noms disponibles.

A tout moment de l'exécution, il y a il existe au moins trois scopes imbriqués dont les dont les espaces de noms sont directement accessibles : l'étendue la plus interne, qui est recherchée d'abord, contient les noms locaux ; les les espaces de noms de toutes les fonctions englobantes, qui sont recherchées en commençant par la fonction la plus proche ; l'espace intermédiaire qui est recherchée ensuite, contient les noms contient les noms globaux du module actuel ; et l'étendue la plus extérieure (recherchée en dernier) est l'espace de nom contenant les noms de fonctions intégrées. contenant les noms intégrés.

Il existe deux fonctions : globals y locals qui vous montrent le contenu de deux de ces espaces de noms.

Les espaces de noms sont créés par les paquets, les modules, les classes, la construction d'objets et les 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 nom local ou l'espace de nom global.

Local, dans ce cas, est le corps de la fonction de la méthode. Foo.spam .

Global est -- eh bien -- global.

La règle est de rechercher les espaces locaux imbriqués créés par les fonctions des méthodes (et les définitions des fonctions imbriquées), puis de rechercher les espaces globaux. C'est tout.

Il n'y a pas d'autres portées. Le site for (et d'autres instructions composées comme if y try ) ne créent pas de nouveaux scopes imbriqués. Seules les définitions (paquets, modules, fonctions, classes et instances d'objets.)

À l'intérieur d'une définition de classe, les noms font partie de l'espace de noms de la classe. code2 par exemple, doivent être qualifiés par le nom de la classe. En général, Foo.code2 . Cependant, self.code2 fonctionnera également parce que les objets Python considèrent la classe contenante comme une solution de repli.

Un objet (une instance d'une classe) possède des variables d'instance. Ces noms sont dans l'espace de nom de l'objet. Elles doivent être qualifiées par l'objet. ( variable.instance .)

À l'intérieur d'une méthode de classe, vous avez des locaux et des globaux. Vous dites self.variable pour choisir l'instance comme espace de nom. Vous noterez que self est un argument de chaque fonction membre de la classe, ce qui en fait une partie de l'espace de noms local.

Véase Règles d'application de Python , Portée de Python , Portée variable .

5 votes

Ceci n'est plus d'actualité. Depuis la version 2.1 (il y a 7 ans), il y a plus de deux portées, car les fonctions imbriquées introduisent de nouvelles portées. Ainsi, une fonction à l'intérieur d'une fonction aura accès à sa portée locale, à la portée de la fonction englobante et à la portée globale (également les builtins).

0 votes

Je suis désolé, ce n'est plus le cas. Python has two namespaces available. Global and local-to-something.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X