Подтвердить что ты не робот

Globals и locals в python exec()

Я пытаюсь запустить кусок кода python с помощью 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

что приводит к следующему выводу

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

Однако, если я изменю код на это -

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

тогда он отлично работает - дает следующий вывод -

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

Ясно, что A присутствует и доступно - что происходит в первом фрагменте кода? Я использую 2.6.5, приветствия,

Колин

* ОБНОВЛЕНИЕ 1 *

Если я проверил locals() внутри класса -

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

Тогда становится ясно, что locals() не одинаковы в обоих местах -

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

Однако, если я это сделаю, проблем нет -

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

  class B(object):
    a_ref = A

f()

print 'Finished OK'

* ОБНОВЛЕНИЕ 2 *

ok, поэтому документы здесь - http://docs.python.org/reference/executionmodel.html

'Определение класса - это исполняемый оператор, который может использовать и определять имена. Эти ссылки соответствуют нормальным правилам разрешения имен. Пространство имен определения класса становится атрибутом словаря класса. Имена, определенные в области класса, не видны в методах. '

Мне кажется, что "A" должно быть доступно как свободная переменная внутри исполняемого оператора, который является определением B, и это происходит, когда мы вызываем f() выше, но не тогда, когда мы используем exec(). Это можно более легко показать со следующим -

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
"""

который выводит

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

Итак, я думаю, новый вопрос: почему не те локальные жители подвергаются как свободные переменные в определениях функций и классов - это похоже на довольно стандартный сценарий закрытия.

4b9b3361

Ответ 1

Ну, я считаю, что это ошибка реализации или недокументированное дизайнерское решение. Суть проблемы в том, что операция привязки имен в области модуля должна связываться с глобальной переменной. То, как это достигается, заключается в том, что когда на уровне модуля globals() IS locals() (попробуйте сделать это в интерпретаторе), поэтому, когда вы выполняете какое-либо связывание имен, он назначает его, как обычно, местным жителям (), который также является глобальным, поэтому создается глобальная переменная.

Когда вы просматриваете переменную, вы сначала проверяете свои текущие локали, и если имя не найдено, вы рекурсивно проверяете локали содержащихся областей для имени переменной, пока не найдете эту переменную или не достигнете области модуля. Если вы достигнете этого, вы проверите глобальные переменные, которые должны быть локальными областями видимости модуля.

>>> 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

Такое поведение является причиной того, что наследование сработало (имя-поиск использовал объект-объект scope locals(), который действительно имел в нем A).

В конце концов, это уродливый взлом в реализации CPython, поиск по глобальным глобальным подсчетам. Это также вызывает некоторые бессмысленные искусственные ситуации - например:

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

Обратите внимание, что это мой вывод, основанный на испорчении интерпретатора при чтении раздела 4.1 (Именование и привязка) ссылки на язык python. Хотя это не является окончательным (я не открыл источники CPython), я уверен, что я прав в отношении поведения.

Ответ 2

После print locals() и globals() вы найдете причину, по которой exec выдает исключение "не определено", и вы можете попробовать это

d = dict(locals(), **globals())
exec (code, d, d)

Ответ 3

Если ваш вопрос заключается в том, как заставить оператор exec вести себя как область файла, я следил за некоторыми подсказками в связанном вопросе и ошибке и заставлял его работать, передавая один словарь для глобальных и локальных пользователей. По-видимому, область файлов - это особый случай, когда локальные объявления автоматически помещаются в глобальную область.

exec code in dict()

Ответ 4

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

печатает

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

Хокетт, вы говорите,

Основная причина, по которой я хотел использовать локали, вроде этого, заключалась в том, чтобы получить все содержимое, определенное в строке кода, без всех других вещей, которые python помещает в глобальные переменные.

С exec, если ваши глобальные значения не определены __builtins__, exec добавляет один элемент, __builtins__ к вашим глобальным переменным, поэтому вы получаете A, B и __builtins__. __builtins__ сам является большим словарем, но всегда остается один и тот же один элемент для удаления (до тех пор, пока вы дождитесь, когда ваш код будет закончен, прежде чем вы его удалите!). Документировано в разделе exec() здесь.

Документы для eval под встроенные функции say

Если словарь globals присутствует и не имеет встроенных встроенных функций, текущие глобальные скопированные файлы копируются в глобальные переменные до того, как выражение будет проанализировано.

Но на самом деле кажется, что только скопировать __builtins__ in.

(И n.b., что все остальные сказали: либо установить глобальные и локальные, либо сказать exec my_code_AST in global_env без отдельного local_env.)