web-dev-qa-db-fra.com

globaux et locaux dans python exec ()

J'essaie d'exécuter un morceau de code python en utilisant exec.

my_code = """
class A(object):
  pass

print 'locals: %s' % locals()
print 'A: %s' % A

class B(object):
  a_ref = A
"""

global_env = {}
local_env = {}
my_code_AST = compile(my_code, "My Code", "exec")
exec(my_code_AST, global_env, local_env)

print local_env

ce qui se traduit par la sortie suivante

locals: {'A': <class 'A'>}
A: <class 'A'>
Traceback (most recent call last):
  File "python_test.py", line 16, in <module>
    exec(my_code_AST, global_env, local_env)
  File "My Code", line 8, in <module>
  File "My Code", line 9, in B
NameError: name 'A' is not defined

Cependant, si je change le code en ceci -

my_code = """
class A(object):
  pass

print 'locals: %s' % locals()
print 'A: %s' % A

class B(A):
  pass
"""

global_env = {}
local_env = {}
my_code_AST = compile(my_code, "My Code", "exec")
exec(my_code_AST, global_env, local_env)

print local_env

alors cela fonctionne bien - donnant la sortie suivante -

locals: {'A': <class 'A'>}
A: <class 'A'>
{'A': <class 'A'>, 'B': <class 'B'>}

De toute évidence, A est présent et accessible - qu'est-ce qui ne va pas dans le premier morceau de code? J'utilise 2.6.5, cheers,

Colin

* MISE À JOUR 1 *

Si je vérifie les locaux () à l'intérieur de la classe -

my_code = """
class A(object):
  pass

print 'locals: %s' % locals()
print 'A: %s' % A

class B(object):
  print locals()
  a_ref = A
"""

global_env = {}
local_env = {}
my_code_AST = compile(my_code, "My Code", "exec")
exec(my_code_AST, global_env, local_env)

print local_env

Ensuite, il devient clair que les locaux () ne sont pas les mêmes aux deux endroits -

locals: {'A': <class 'A'>}
A: <class 'A'>
{'__module__': '__builtin__'}
Traceback (most recent call last):
  File "python_test.py", line 16, in <module>
    exec(my_code_AST, global_env, local_env)
  File "My Code", line 8, in <module>
  File "My Code", line 10, in B
NameError: name 'A' is not defined

Cependant, si je fais cela, il n'y a pas de problème -

def f():
  class A(object):
    pass

  class B(object):
    a_ref = A

f()

print 'Finished OK'

* MISE À JOUR 2 *

ok, donc les documents ici - http://docs.python.org/reference/executionmodel.html

'Une définition de classe est une instruction exécutable qui peut utiliser et définir des noms. Ces références suivent les règles normales de résolution de noms. L'espace de noms de la définition de classe devient le dictionnaire d'attributs de la classe. Les noms définis au niveau de la classe ne sont pas visibles dans les méthodes. '

Il me semble que 'A' devrait être disponible en tant que variable libre dans l'instruction exécutable qui est la définition de B, et cela se produit lorsque nous appelons f() ci-dessus, mais pas lorsque nous utilisons exec (). Cela peut être plus facilement montré avec ce qui suit -

my_code = """
class A(object):
  pass

print 'locals in body: %s' % locals()
print 'A: %s' % A

def f():
  print 'A in f: %s' % A

f()

class B(object):
  a_ref = A
"""

qui sort

locals in body: {'A': <class 'A'>}
A: <class 'A'>
Traceback (most recent call last):
  File "python_test.py", line 20, in <module>
    exec(my_code_AST, global_env, local_env)
  File "My Code", line 11, in <module>
  File "My Code", line 9, in f
NameError: global name 'A' is not defined

Donc, je suppose que la nouvelle question est - pourquoi ces sections locales ne sont-elles pas exposées en tant que variables libres dans les fonctions et les définitions de classe - cela semble être un scénario de fermeture assez standard.

35
hawkett

Eh bien, je pense que c'est soit un bogue d'implémentation, soit une décision de conception non documentée. Le nœud du problème est qu'une opération de liaison de nom dans la portée du module doit se lier à une variable globale. La façon dont cela est réalisé est que, au niveau du module, globals () IS locals () (essayez-le dans l'interpréteur), donc lorsque vous effectuez une liaison de nom, il affecte comme d'habitude, au dictionnaire locals (), qui est aussi les globaux, donc une variable globale est créée.

Lorsque vous recherchez une variable, vous vérifiez d'abord vos sections locales actuelles, et si le nom n'est pas trouvé, vous vérifiez récursivement que les sections locales contiennent des étendues pour le nom de la variable jusqu'à ce que vous trouviez la variable ou atteigniez la portée du module. Si vous atteignez cela, vous vérifiez les globaux, qui sont censés être les locaux de la portée du module.

>>> exec(compile("import sys\nprint sys._getframe().f_code.co_name", "blah", "exec"), {}, {})
<module>
>>> exec("a = 1\nclass A(object):\n\tprint a\n", {}, {})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 2, in <module>
  File "<string>", line 3, in A
NameError: name 'a' is not defined
>>> d = {}
>>> exec("a = 1\nclass A(object):\n\tprint a\n", d,d)
1

Ce comportement explique pourquoi l'héritage a fonctionné (la recherche de nom a utilisé la portée locale de l'objet de code (), qui contenait en effet A)).

En fin de compte, c'est un vilain piratage dans la mise en œuvre de CPython, une recherche globale de casse spéciale. Cela provoque également des situations artificielles absurdes - par exemple:

>>> def f():
...     global a
...     a = 1
...
>>> f()
>>> 'a' in locals()
True

Veuillez noter que ceci est toute mon inférence basée sur le fait de jouer avec l'interprète lors de la lecture de la section 4.1 (Attribution de noms et liaisons) de la référence de langage python. Bien que ce ne soit pas définitif (je n'ai pas ouvert Sources de CPython), je suis assez sûr d'avoir raison sur le comportement.

18
Roee Shenberg

Après print locals() et globals(), vous trouverez la raison pour laquelle exec lève une exception "non définie", et vous pouvez essayer ceci

d = dict(locals(), **globals())
exec (code, d, d)
9
Zoe
my_code = """
class A(object):
    pass

class B(object):
    a = A
"""

my_code_AST = compile(my_code, "My Code", "exec")
extra_global = "hi"
global_env = {}
exec my_code_AST in global_env
print "global_env.keys() =", global_env.keys()
print "B.a =", global_env["B"].a

impressions

global_env.keys() = ['__builtins__', 'A', 'B']
B.a = <class 'A'>

Hawkett, vous dites,

la principale raison pour laquelle je voulais utiliser des sections locales comme ça, était d'obtenir toutes les choses définies dans la chaîne de code, sans toutes les autres choses que python met dans les globales.

Avec exec, si vos globaux n'ont pas __builtins__ défini, exec ajoute un élément, __builtins__ à vos globaux, vous obtenez donc A, B et __builtins__. __builtins__ lui-même est un gros dictionnaire, mais c'est toujours le même élément à supprimer (tant que vous attendez que votre code ait fini de l'utiliser avant de le supprimer!). Documenté sous exec () ici .

Les documents pour eval sous fonctions intégrées say

Si le dictionnaire des globaux est présent et qu'il manque ‘ builtins ’, les globaux actuels sont copiés dans les globaux avant que l’expression ne soit analysée.

Mais en réalité, il ne semble que copier __builtins__ dans.

(Et n.b. ce que tout le monde a dit: soit définir les globaux et les locaux de la même manière ou dire exec my_code_AST in global_env sans local_env séparé.)

2
SteveWithamDuplicate

Si votre question est de savoir comment obtenir que l'instruction exec se comporte comme la portée du fichier, j'ai suivi quelques conseils dans la question et le bug liés et je l'ai fait fonctionner en passant un seul dictionnaire pour les globaux et les locaux. Apparemment, la portée du fichier est un cas spécial où les déclarations locales sont automatiquement placées dans la portée globale.

exec code in dict()
2
Don Kirkby