Почему exec () работает по-разному, когда вызывается внутри функции и как этого избежать - PullRequest
4 голосов
/ 19 апреля 2019

Я пытаюсь объявить две функции в операторе exec в python. Давайте назовем их f1 () и f2 () .

Я обнаружил, что когда exec вызывается внутри некоторой функции, тогда f2 () не имеет видимости f1 () . Однако этого не происходит, когда exec и вызов функции помещаются в глобальный код.

# Case 1: Working fine

code = """
def f1(): print "bar"
def f2(): f1()
"""

exec(code)
f2() # Prints "bar" as expected
# Case 2: Throws NameError: global name 'f1' is not defined

code = """
def f1(): print "bar"
def f2(): f1()
"""

def foo():
    exec(code)
    f2() # NameError

foo()

Может кто-нибудь объяснить мне, как избежать этого NameError и заставить exec работать внутри функции?

Ответы [ 3 ]

5 голосов
/ 19 апреля 2019

exec() принимает второй параметр для globals. Как объяснено в документах :

Примечание. Встроенные функции globals () и locals () возвращают текущий глобальный и локальный словарь, соответственно, которые могут быть полезны для передачи в качестве второго и третьего аргумента exec ().

Таким образом, вы можете сделать эту работу, явно передав globals():

code = """
def f1(): print ("bar")
def f2(): f1()
"""

def foo():
    exec(code, globals())
    f2() # works in python2.7 and python3

foo()

Если вы хотите точно контролировать область, вы можете передать объект в exec:

code = """
def f1(): print ("bar")
def f2(): f1()
"""

def foo():
    context = {}
    exec(code, context)
    context['f2']() 

foo() 
2 голосов
/ 19 апреля 2019

Комментарий Кевина хорош и заслуживает некоторого повторения - использование exec опасно, почти всегда есть лучший подход.

Однако, в ответ на ваш вопрос в первом случае, f1 () и f2 () находятся в глобальном пространстве имен, поэтому при вызове f2 () он может найти f1 (). Во втором случае они создаются в локальном пространстве функции foo (). Когда вызывается f2 (), он не может найти локальное определение f1 ().

Вы можете исправить это, используя:

code = """
global f1
def f1(): print "bar"
def f2(): f1()
"""
def foo():
    exec(code)
    f2()
foo()

Опять же - это почти наверняка не тот способ, которым вы хотите решить эту проблему.

** РЕДАКТИРОВАТЬ ** Публикуется неверная версия кода, который я проверял, эта версия - то, что я хотел включить.

0 голосов
/ 19 апреля 2019

"Во всех случаях, если опциональные части [из exec ()] опущены, код выполняется в текущей области."

https://docs.python.org/3.5/library/functions.html#exec

...