Использование exec() с рекурсивными функциями
Вопрос
Я хочу выполнить некоторый код Python, введенный во время выполнения, поэтому я получаю строку и вызываю
exec(pp, globals(), locals())
где пп это строка.Он работает нормально, за исключением рекурсивных вызовов, например.например, этот код в порядке:
def horse():
robot.step()
robot.step()
robot.turn(-1)
robot.step()
while True:
horse()
А вот этого нет:
def horse():
robot.step()
robot.step()
robot.turn(-1)
robot.step()
horse()
horse()
ИмяОшибка:Глобальное название «лошадь» не определена
Есть ли способ запустить рекурсивный код?
ОБНОВЛЯТЬ
a = """\
def rec(n):
if n > 10:
return
print n
return rec(n+1)
rec(5)"""
exec(a)
Работает, если поставить на верхний уровень.Но если переместиться внутрь функции:
def fn1():
a = """\
def rec(n):
if n > 10:
return
print n
return rec(n+1)
rec(5)"""
exec(a)
fn1()
возникает та же ошибка:ИмяОшибка:глобальное имя 'rec' не определено
Решение
Меня устраивает:
a = """\
def rec(n):
if n > 10:
return
print n
return rec(n+1)
rec(5)"""
exec(a)
5
6
7
8
9
10
Все, что я могу сказать, это то, что, вероятно, в вашем коде есть ошибка.
Редактировать
Ну вот
def fn1():
glob = {}
a = """\
def rec(n):
if n > 10:
return
print n
return rec(n+1)
rec(5)"""
exec(a, glob)
fn1()
Другие советы
Меня это тоже поначалу удивило и кажется странным крайним случаем, когда exec не действует ни как определение верхнего уровня, ни как определение внутри включающей функции.Похоже, что происходит то, что определение функции выполняется в переданном вами словаре locals().Однако определенная функция на самом деле не имеет доступа к этому локальному словарю.
Обычно, если вы определяете функцию на верхнем уровне, локальные и глобальные переменные совпадают, поэтому функции видны внутри, поскольку они могут видеть функцию в глобальных переменных.
Когда функция определена внутри области действия другой функции, Python заметит, что к ней обращаются внутри функции, и создаст замыкание, чтобы «лошадь» сопоставлялась с привязкой во внешней области.
Здесь это странный половинчатый случай.exec действует так, как будто определения находятся на верхнем уровне, поэтому замыкания не создаются.Однако, поскольку локальные переменные — это не то же самое, что глобальные переменные, определение не применяется там, где функция может получить к ним доступ — оно определяется только в недоступном внешнем локальном словаре.
Есть несколько вещей, которые вы можете сделать:
- Используйте один и тот же словарь как для локальных, так и для глобальных значений.то есть "
exec s in locals(),locals()
" (или лучше, просто используйте свой собственный словарь).Предоставление только слова globals() имеет тот же эффект, т.е. "exec s in mydict
" # Поместите функцию внутри собственной функции, чтобы было создано замыкание.например
s=""" def go(): def factorial(x): if x==0: return 1 return x*factorial(x-1) print factorial(10) go()"""
Заставьте функцию перейти в globals(), а не в locals, поместив директиву «global funcname», как предложено ответ Стефана
Это работает для меня (добавлено global rec
). rec(5)
звонит местному rec
, но rec(n+1)
без него вызывает глобальную запись (которой не существует).
def fn1():
a = """global rec
def rec(n):
if n > 10:
return
print n
return rec(n+1)
rec(5)"""
exec(a)
«ИмяОшибка:глобальное имя «rec» не определено» означает, что он ищет запись в глобальной области, а не в локальной.Похоже, он определяет запись в локальной области, но затем пытается выполнить ее в глобальной.Попробуйте напечатать locals() и globals() внутри выполняемой строки.