Python中的递归有多安全?
Posted
技术标签:
【中文标题】Python中的递归有多安全?【英文标题】:How safe is recursion in Python? 【发布时间】:2011-06-29 10:02:25 【问题描述】:我正在做一项 AI 作业,尽管我的教授提出了建议,但我无意用 lisp 写这个作业。但是,我确实想递归地编写它,以使其简洁明了。这是我的问题:
如果我在大型状态空间上执行搜索,我是否会面临堆栈空间不足的重大风险? Python 堆栈有多深?
【问题讨论】:
你为什么不首先使用尾调用递归?堆栈溢出应该不是问题。 用lisp写是个好主意。 @Don Roby:获得学分可能是一个更好的主意,但是以 两种 语言解决程序最好恕我直言。让 OP 在 Python 中尝试。根据我的经验,由于标准库中提供了高效的 FIFO 和优先级队列,因此更复杂的“经典 AI”搜索算法会更容易。 用与教授建议的语言不同的语言来做这个项目从来都不是一个好主意。 1) 他/她可能有充分的理由选择它(有时只是为了向您展示另一种语言,因此它可能不是解决项目最简单的语言) 2) 这是他们期望 GRADE 的语言。通过使用与所有同学不同的语言来完成项目,你会让你的教授的生活变得更加困难,她/他可能会因为你的努力而给你打分。 @Larsmans:我不是不同意你,而是不同意 OP。我同意用多种语言做任何项目几乎总是能提供信息。 (Alex 说他“无意”使用 LISP,这是我遇到的问题......)也许我应该说“提交项目......”。 【参考方案1】:Python(即 CPython)中的递归不能比sys.getrecursionlimit()
更深入。您可以使用 sys.setrecursionlimit
将此限制设置为不同的值。
我看到三个选项:
-
毕竟使用 Lisp,它针对递归问题进行了优化(智能 Lisp 编译器将消除尾递归)
在递归函数中设置递归限制以获得无限递归(有程序吃掉flaming death的风险)
使用带有显式堆栈的迭代。一个简单的
list
就可以了。 append
是推送,pop
是流行。
【讨论】:
而且,与迭代版本相比,它的速度相当慢 :) @Ant:如果你必须检查并重置算法内部的递归限制,它会变得更慢,更不用说那个解决方案的问题了。【参考方案2】:>>> i=0
>>> def a():
... global i
... i += 1
... try:
... a()
... except:
... print(i)
...
>>> a()
999
>>> import sys
>>> sys.getrecursionlimit()
1000
不确定是否有帮助
【讨论】:
【参考方案3】:Python 堆栈有多深?
python 中默认的递归限制是 1000 帧。您可以使用sys.setrecursionlimit(n)
更改它,风险自负。
如果您打算使用 python,我建议您使用更适合该语言的模式。如果你想使用递归样式搜索,并且需要任意堆栈深度,你可以利用 python 的增强生成器(协程)来创建一个“蹦床模式”(PEP342 中有一个例子)
尽管我的教授提出了建议,但我无意用 lisp 写这个作业
如果该练习旨在基于递归,那么尾调用优化语言(如 lisp)可能是您的最佳选择。
【讨论】:
【参考方案4】:Python 将递归深度限制在一个可以通过调用 sys.getrecursionlimit 找到并通过调用 sys.setrecursionlimit 进行更改的值。默认值不是很大。如果你将它设置得太高,那么当 Python 递归时,你最终可能会炸毁 C 堆栈。 “太高”有多高取决于平台,但默认值在所有平台上都应该是安全的(您会得到 Python 异常而不是崩溃)。
有些语言对尾调用优化做出了强有力的保证。 Scheme 是最著名的例子。它是一种 Lisp 方言。无论您多么不喜欢您的教授,您都应该考虑至少学习一门类似 Lisp 的语言。
【讨论】:
【参考方案5】:正如其他人指出的那样,您可以使用sys.setrecursiondepth()
增加递归深度,尽管有溢出 C 堆栈的风险(假设您使用的是 CPython)。如果你遇到这个问题,你可以通过调用 thread.stack_size()
来创建一个更大的堆栈大小的线程,然后派生一个新的线程来完成实际的工作。
【讨论】:
以上是关于Python中的递归有多安全?的主要内容,如果未能解决你的问题,请参考以下文章
编译后包含在我的 xcode 项目中的 plist 有多安全?
Android 中的 Firebase 本地数据库有多安全?