14 quiz pour comprendre la portée étonnamment compliquée de Python

introduction

La portée variable et le mécanisme de référencement de Python sont étonnamment peu intuitifs, et les débutants peuvent tomber dans le piège. Mais une fois que vous avez appris les règles, ce n'est pas si compliqué. Voici quelques points pour vous aider à comprendre cela.

Pour chaque question, répondez à ** ce qui est produit ou si une erreur est générée **. L'environnement d'exécution est Python 3. Le problème difficile (ou plutôt maniaque) est que le titre est rouge </ font>.

question 1

x = 1

if True:
    x = 2

print(x)

répondre

2

En Python, l'instruction ʻif` ne forme pas de portée. Par conséquent, «x» dans l'instruction «if» est la même variable que le «x» extérieur.

Problème 2

for i in range(10):
    x = i * 2

print(i, x)

répondre

9 18

Comme ʻif, l'instruction forne forme pas de portée, donc les variables de boucle et les variables définies en interne sont accessibles après l'instructionfor`.

Problème 3

ls = [x * 2 for x in range(10)]

print(x)

répondre

(print(x)À l'endroit)
NameError: name 'x' is not defined

Lorsque la compréhension de liste est exécutée, une nouvelle portée est créée et des variables de boucle y sont définies. Par conséquent, «X» n'est pas accessible à partir de «print (x)» existant dans la portée externe.

Problème 4

funs = []

for i in range(5):
    def f():
        print(i)
    funs.append(f)

for fun in funs:
    fun()

répondre

4
4
4
4
4

La variable externe ʻi est référencée depuis l'intérieur de la fonction f, mais la valeur de ʻi au moment où ** print (i) est exécuté est utilisée **. Les cinq fs sont exécutés après l'instruction for, à quel point ʻi est égal à 4, donc ils affichent tous 4`.

La même chose se produit avec la notation d'inclusion de liste.

Problème 5

x = 0

def f():
    x = 1

f()
print(x)

répondre

0

Puisque le bloc [^ block] formé par la fonction f a une ** instruction d'affectation ** de x = 1, une nouvelle variable de x sera créée dans la portée de ce bloc. Autrement dit, le «x» de niveau supérieur n'est pas modifié par «f».

[^ block]: block est un ensemble de code correspondant à une portée. , Une portée est formée pour chaque bloc. Ce sont les fonctions, les modules (c'est-à-dire le niveau supérieur) et les définitions de classe qui forment les blocs.

Problème 6

x = 0

def f():
    print(x)
    x = 1

f()

répondre

(print(x)À l'endroit)
UnboundLocalError: local variable 'x' referenced before assignment

Semblable au problème précédent, le bloc formé par la fonction f a une ** instruction d'affectation ** qui dit x = 1, donc la portée est x avant ** avant que le bloc ne soit exécuté. Une variable appelée x est créée. Cependant, lorsque print (x) est exécuté, x n'est pas lié, donc une exception est levée.

Problème 7

x = 0

def f():
    x += 1

f()
print(x)

répondre

(print(x)À l'endroit)
UnboundLocalError: local variable 'x' referenced before assignment

Puisque «+ =» est également considéré comme une instruction d'affectation, la variable «x» est créée dans le bloc formé par «f» comme dans le problème précédent. Cependant, lorsque «x + = 1» est exécuté, la valeur de «x» n'existe pas, donc une exception est levée.

Vous devez utiliser global ou nonlocal pour changer la valeur des variables dans la portée externe.

Problème 8

x = 0

def f():
    if False:
        x = 1
    print(x)

f()

répondre

(print(x)À l'endroit)
UnboundLocalError: local variable 'x' referenced before assignment

Puisque l'instruction ʻif ne forme pas un bloc, il y a une instruction d'affectation à xdans le bloc formé parf comme dans le problème précédent, et la variable x` est créée. Cependant, en réalité, une valeur n'est pas affectée à «x», donc une exception se produit.

Problème 9 </ font>

x = 0

def f():
    del x

f()

répondre

(À la place de del x)
UnboundLocalError: local variable 'x' referenced before assignment

En fait, l'instruction del a pour effet de créer une variable dans le bloc, tout comme l'instruction d'affectation. Par conséquent, la variable «x» est créée dans le bloc formé par «f», mais une exception se produit car «del x» est exécuté sans que la valeur de «x» existe.

La syntaxe qui a pour effet de créer des variables de cette manière est l'instruction d'affectation, l'instruction del, l'instruction for, la définition de classe, la définition de fonction, l'instruction ʻimport et les avec et ʻexcept ʻas`.

Problème 10

x = 0

def f():
    def g():
        print(x)
    x = 1
    g()

f()

répondre

1

Lorsque «g» est exécuté, «1» est affiché car «x» existe dans la portée externe («f») et la valeur est «1».

Problème 11 </ font>

x = 0

def f():
    x = 1
    del x
    def g():
        print(x)
    g()

f()

répondre

(print(x)À l'endroit)
NameError: free variable 'x' referenced before assignment in enclosing scope

Même si del x est exécuté, x continue d'exister dans la portée de f (seule la valeur disparaît). Par conséquent, «x» dans «g» fait référence à «x» défini par «f». Mais ce «x» n'a aucune valeur, donc une exception se produit.

Pour la même raison, les deux codes suivants donnent le même résultat.

x = 0

def f():
    x = 1
    def g():
        print(x)
    del x
    g()

f()
x = 0

def f():
    def g():
        print(x)
    x = 1
    del x
    g()

f()

Problème 12 </ font>

x = 1

def f():
    x = 2
    try:
        raise Exception()
    except Exception as x:
        pass

    def g():
        print(x)
    g()

f()

répondre

(print(x)À l'endroit)
NameError: free variable 'x' referenced before assignment in enclosing scope

Lorsqu'une exception se produit dans l'instruction try et que la clause ʻexcept est exécutée, la valeur spécifiée dans ʻas est ** supprimée ** comme dans del **. Par conséquent, lorsque g est exécuté, x dans f existe mais n'a pas de valeur, donc une exception se produit.

Si la clause ʻexcept` n'est pas exécutée, la variable correspondante n'est pas supprimée.

Problème 13

x = 1

class C:
    x = 2
    def f():
        print(x)
    f()

répondre

1

Le code de la définition de classe est spécial par rapport à la portée, et les variables qui y sont définies (variables de classe) ne sont accessibles qu'à partir de cette portée, ** pas directement à partir des fonctions (c'est-à-dire des méthodes) dans la définition de classe **.

Problème 14 </ font>

x = 0

def f():
    x = 1
    def g():
        print(eval("x"))
    g()

f()

répondre

0

Ceci est un problème avec le comportement (non intuitif) de la fonction ʻeval. Les seules variables accessibles à partir du code exécuté par la fonction ʻeval sont en fait ** "variables globales" et "variables définies dans le bloc où ʻeval est exécuté **", et les variables définies entre elles Est inaccessible. Dans le code actuel, les variables (non intégrées) auxquelles on peut accéder à partir du code passé à la fonction ʻeval sont les variables (pas même une) définies dans le "x" et "g" sur la première ligne. Par conséquent, le «x» de niveau supérieur est référencé et «0» est affiché.

La fonction ʻexec` se comporte de la même manière.

référence

Recommended Posts