在python中是否有方法检查函数是否是递归的?

在python中是否有方法检查函数是否是递归的?,python,recursion,python-internals,Python,Recursion,Python Internals,我想为练习编写一个测试函数,以确保函数正确实现。 所以我想知道,有没有一种方法,给定一个函数“foo”,来检查它是否是递归实现的 如果它封装了一个递归函数并使用它,那么它也会起作用。例如: def foo(n): def inner(n): #more code inner(n-1) return inner(n) 这也应该被认为是递归的 请注意,我想使用外部测试功能来执行此检查。不改变函数的原始代码。解决方案: from bdb import

我想为练习编写一个测试函数,以确保函数正确实现。
所以我想知道,有没有一种方法,给定一个函数“foo”,来检查它是否是递归实现的
如果它封装了一个递归函数并使用它,那么它也会起作用。例如:

def foo(n):
    def inner(n):
        #more code
        inner(n-1)
    return inner(n)
这也应该被认为是递归的
请注意,我想使用外部测试功能来执行此检查。不改变函数的原始代码。

解决方案:

from bdb import Bdb
import sys

class RecursionDetected(Exception):
    pass

class RecursionDetector(Bdb):
    def do_clear(self, arg):
        pass

    def __init__(self, *args):
        Bdb.__init__(self, *args)
        self.stack = set()

    def user_call(self, frame, argument_list):
        code = frame.f_code
        if code in self.stack:
            raise RecursionDetected
        self.stack.add(code)

    def user_return(self, frame, return_value):
        self.stack.remove(frame.f_code)

def test_recursion(func):
    detector = RecursionDetector()
    detector.set_trace()
    try:
        func()
    except RecursionDetected:
        return True
    else:
        return False
    finally:
        sys.settrace(None)
使用/测试示例:

def factorial_recursive(x):
    def inner(n):
        if n == 0:
            return 1
        return n * factorial_recursive(n - 1)
    return inner(x)


def factorial_iterative(n):
    product = 1
    for i in xrange(1, n+1):
        product *= i
    return product

assert test_recursion(lambda: factorial_recursive(5))
assert not test_recursion(lambda: factorial_iterative(5))
assert not test_recursion(lambda: map(factorial_iterative, range(5)))
assert factorial_iterative(5) == factorial_recursive(5) == 120

本质上,
test\u递归
接受一个不带参数的可调用函数,调用它,并返回
True
如果在执行该可调用函数的过程中,相同的代码在堆栈中出现两次,则返回
False
。我想很有可能这不是OP想要的。它可以很容易地进行修改,以测试相同的代码是否在某个特定时刻出现在堆栈中10次。

我还没有亲自验证Alex的答案是否有效(尽管我认为它有效,并且比我即将提出的要好得多),但是如果您想要比这更简单(更小)的东西,您只需使用
sys.getrecursionlimit()
手动将其出错,然后在函数中检查该错误。例如,这是我为自己的递归验证编写的:

import sys

def is_recursive(function, *args):
  try:
    # Calls the function with arguments
    function(sys.getrecursionlimit()+1, *args)
  # Catches RecursionError instances (means function is recursive)
  except RecursionError:
    return True
  # Catches everything else (may not mean function isn't recursive,
  # but it means we probably have a bug somewhere else in the code)
  except:
    return False
  # Return False if it didn't error out (means function isn't recursive)
  return False
虽然它可能不那么优雅(在某些情况下更容易出错),但它比Alex的代码小得多,并且在大多数情况下工作得相当好。这里的主要缺点是,使用这种方法,您使您的计算机处理函数经过的每个递归,直到达到递归限制。我建议使用
sys.setrecursionlimit()
临时更改递归限制,同时使用此代码最小化递归处理所需的时间,如下所示:

sys.setrecursionlimit(10)
if is_recursive(my_func, ...):
  # do stuff
else:
  # do other stuff
sys.setrecursionlimit(1000) # 1000 is the default recursion limit

假设您定义了调用
foo()
foo()
。然后说
bar=foo
,并定义一个新的
foo()
。当您调用
bar()
时,它调用的是另一个
foo
,而不是它本身。您可能会说该函数是递归函数,因为它调用的函数有其名称,但它可能是具有相同名称的另一个函数。@AlexHall:但这是在Python中检查递归的唯一方法,当它实际发生时。因为递归调用所使用的名称在任何时候都可能被重新定义为其他名称。@MartijnPieters至少可以找到一个在大多数情况下都有效的OP问题解决方案。宗多的反对意见非常明确,对OP来说并不重要。@Arthur.V:答案是这是不可能做到的。这只能猜测。对高度动态代码的静态分析总是局限于猜测。知道的唯一方法是跟踪(这是副本中的答案所做的),您可以在运行时通过将递归限制设置为100,然后运行一个函数,该函数的值应该执行500次递归;看到它失败,然后恢复递归限制,并看到它返回正确的值