1

我有一个脚本,可以递归地分析链接中的 javascript,所以如果它找到一个 javascript,那么它会分析 javascript,如果它正在分析的 javascript 包含更多的 javascript,那么它会继续运行,依此类推。但是,我遇到了这个递归永远不会停止的问题,有没有办法为这个递归添加超时?

4

4 回答 4

6

Python 有一个内置的递归限制,RuntimeError如果超过这个限制,就会引发 a。默认情况下,它的堆栈限制为 1000。所以:

try:
    func_that_may_recurse_infinitely()   # i.e., your JavaScript crawler func
except RuntimeError as e:
    if "recursion" in str(e):
        print "stop all the downloadin'!"

sys.setrecursionlimit()如果需要更深或更浅,您可以修改初始递归限制。

但是,更好的方法可能是保留set()您已经看到的项目,并简单地拒绝处理您已经处理的任何项目。这可以防止您首先陷入递归情况。

于 2012-07-17T18:10:11.243 回答
2

我倾向于同意kindall。但是,如果您确实想限制递归的深度,则可以执行以下操作:

def foo(max_depth = 10, cur_depth=0):
    if cur_depth >= max_depth:
        return BASE_CASE

    else:
        return foo(max_depth, cur_depth+1)
于 2012-07-17T18:14:08.677 回答
2

一个快速而肮脏的解决方案是调用 time.time() 并进行比较。例如,假设您有一个简单的阶乘函数,如下所示:

def fact(i):
  if i == 0 or i == 1: return 1
  return i * fact(i-1)

如果您调用 fact(-1),这将旋转一段时间,然后由于最大递归深度而引发 RuntimeError。

您可以像这样添加超时:

import time

def factWithTimeout(i, timeout):
  def fact(i, endtime):
    if time.time() > endtime:
      raise RuntimeError('Timeout')
    if i == 0 or i == 1: return 1
    return i * fact(i-1, endtime)
  return fact(i, time.time() + timeout)

现在,如果您调用factWithTimeout(-1, 0.0001),它只会旋转大约 100us,然后由于超时而退出 RuntimeError。

显然,对于一个如此微不足道的函数,它会在一毫秒内达到递归限制,这并没有太大的不同,但对于一个更现实的函数,这将不是问题。

于 2012-07-17T18:20:43.563 回答
1

你可以这样做:

import time

start = time.time()
timeout_limit = 30   # 30 seconds, or some other number.

def foo():
    if time.time() > start + timeout_limit:
        return 0

    # insert code here.

    foo()

...如果你不想要全局变量,你可以试试这个:

class Foo(object):
    def __init__(self, timeout_limit):
        self.timeout_limit = timeout_limit

    def run(self, ...):
        self.start = time.time()
        self._run(...)

    def _run(self, ...):
        if time.time() > self.start + self.timeout_limit:
            return

        # insert code here.

        self._run(...)

...虽然这可能是矫枉过正。

于 2012-07-17T18:13:13.630 回答