#python #python-3.x #recursion
#python #python-3.x #рекурсия
Вопрос:
Почему этот код выдает ошибку: RuntimeError: maximum recursion depth exceeded during compilation
? print_test
никогда не вызывает себя, поэтому я бы подумал, что это не рекурсивная функция.
def print_test():
print("test")
return print_test
print_test() #prints 'test'
print()
#a quick way of writing "print_test()()()()()()()()()()()()()..."
eval("print_test" "()"*10000) #should print 'test' 10000 times
Когда я тестировал ее, она работала в Python 2.7.7rc1, но выдавала ошибку в Python 3.3.5. Pdb выдает короткий стек вызовов, в отличие от высокого, который обычно существует при превышении максимальной глубины рекурсии.
Traceback (most recent call last):
File "/usr/lib/python3.3/pdb.py", line 1662, in main
pdb._runscript(mainpyfile)
File "/usr/lib/python3.3/pdb.py", line 1543, in _runscript
self.run(statement)
File "/usr/lib/python3.3/bdb.py", line 405, in run
exec(cmd, globals, locals)
File "<string>", line 1, in <module>
File "/home/beet/overflow.py", line 1, in <module>
def print_test():
Я задаюсь этим вопросом из любопытства и понимаю, что это не было бы лучшей практикой программирования.
Комментарии:
1. Говорит ли
pdb
вам что-нибудь запуск вашего скрипта?2. Превышено во время компиляции, а не выполнения. Функция не перекачивает стек; вы достигаете какого-то предела в компиляторе байт-кода.
Ответ №1:
Я считаю, что это связано с проблемой # 5765.
Примените жесткий предел рекурсии в компиляторе [начиная с 3.3]
Не уверен на 100%, но этот код выполняется на 3.2.3:
def f():
return f
eval("f" "()" * 10000)
но не работает на моем 3.4.1, что заставляет меня подозревать, что это изменение вызвало это. Если кто-то подтвердит или опровергнет это, это было бы довольно круто.
Ответ №2:
eval
Выполняет компиляцию. Он может обнаружить, что вы хотите пойти глубже с вложенными вызовами, чем sys.getrecursionlimit()
.
Рекурсия может быть как прямой (функция вызывает себя напрямую), так и косвенной (функция вызывается другой функцией, которая, в свою очередь, когда-то вызывалась этой функцией). Но когда глубина вызова больше, чем ожидалось (1000 для реализации Python на момент написания), вызов может быть частью косвенной рекурсии, которая должна идти еще глубже, чтобы проявить себя как рекурсивная. Другими словами, если достигнута максимальная глубина вызова (а глубина разумна, чтобы думать таким образом), можно назвать это рекурсией, которая не может быть выполнена.
Окончательный ответ можно найти в источниках… Но мне было просто любопытно, поэтому я провел следующий эксперимент. Я попытался написать код, который определяет достаточное количество различных функций (с пронумерованными идентификаторами), которые вызывают следующую — за исключением последней, которая фактически не вызывает первую рекурсивно.
def fn1():
print('fn1() is going to call fn2()')
fn2()
def fn2():
print('fn2() is going to call fn3()')
fn3()
def fn3():
print('fn3() does not call anything.')
fn1()
Последняя строка запускает вложенные вызовы и печатает.
fn1() is going to call fn2()
fn2() is going to call fn3()
fn3() does not call anything.
Мы можем сгенерировать исходный код в виде строки, а затем использовать compile
встроенную функцию, а затем exec
ее:
#!python3
import textwrap
n = 1000
print('n =', n)
lst = []
for i in range(1, n 1):
if i == n:
fndef = textwrap.dedent('''
def fn{}():
print('fn{}() does not call anything.')
fn1()
'''.format(i, i))
else:
fndef = textwrap.dedent('''
def fn{}():
print('fn{}() is going to call fn{}()')
fn{}()
'''.format(i, i, i 1, i 1))
if n <= 10:
print(fndef)
lst.append(fndef)
ast = compile('n'.join(lst), '<string>', 'exec')
exec(ast)
Обратите n = 1000
внимание на то, что находится в начале источника. При выполнении и перенаправлении stdout и stderr на файлы, я мог наблюдать:
n = 1000
fn1() is going to call fn2()
fn2() is going to call fn3()
fn3() is going to call fn4()
...
fn992() is going to call fn993()
fn993() is going to call fn994()
fn994() is going to call fn995()
Traceback (most recent call last):
File "a.py", line 28, in <module>
exec(ast)
File "<string>", line 4000, in <module>
File "<string>", line 3, in fn1
File "<string>", line 7, in fn2
File "<string>", line 11, in fn3
...
File "<string>", line 3967, in fn992
File "<string>", line 3971, in fn993
File "<string>", line 3975, in fn994
File "<string>", line 3978, in fn995
RuntimeError: maximum recursion depth exceeded
Вывод: Python вызывает это рекурсией не только во eval
время, когда это рекурсия, но она еще не была выполнена (как показывает вопрос). Python может называть это рекурсией даже в том случае, когда рекурсии на самом деле нет.
Лучший вывод: кого волнует, когда ясно, что код может быть рекурсией или нет, во время компиляции или во время выполнения. Это все равно не сработало бы