Использование exec() с рекурсивными функциями

StackOverflow https://stackoverflow.com/questions/871887

  •  22-08-2019
  •  | 
  •  

Вопрос

Я хочу выполнить некоторый код 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 действует так, как будто определения находятся на верхнем уровне, поэтому замыкания не создаются.Однако, поскольку локальные переменные — это не то же самое, что глобальные переменные, определение не применяется там, где функция может получить к ним доступ — оно определяется только в недоступном внешнем локальном словаре.

Есть несколько вещей, которые вы можете сделать:

  1. Используйте один и тот же словарь как для локальных, так и для глобальных значений.то есть "exec s in locals(),locals()" (или лучше, просто используйте свой собственный словарь).Предоставление только слова globals() имеет тот же эффект, т.е. "exec s in mydict" #
  2. Поместите функцию внутри собственной функции, чтобы было создано замыкание.например

    s="""
    def go():
        def factorial(x):
            if x==0: return 1
            return x*factorial(x-1)
        print factorial(10)
    go()"""
    
  3. Заставьте функцию перейти в 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() внутри выполняемой строки.

Больше информации.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top