Python 方法是不是可以检查它是不是已从自身内部调用?

Posted

技术标签:

【中文标题】Python 方法是不是可以检查它是不是已从自身内部调用?【英文标题】:Can a Python method check if it has been called from within itself?Python 方法是否可以检查它是否已从自身内部调用? 【发布时间】:2011-12-15 13:36:07 【问题描述】:

假设我有一个 Python 函数 ffhelpfhelp 旨在递归调用自身。 f 不应该被递归调用。有没有办法让f 确定它是否被递归调用?

【问题讨论】:

好吧,你不能不从f 打电话给f 吗? Python 的理念是我们都是明智的成年人,因此,请阅读并尊重文档。只需在您的文档中添加注释,说明不应递归调用 f。顺便提一句。 f 是否将任何用户定义的函数作为输入?如果没有,您作为该函数的作者应该能够确保它不会递归调用自身。 @madjar:递归调用可以是间接的:f 调用用户提供的函数k,而后者又再次调用f @Björn Pollex 一针见血。间接层很多,要保证某些子函数不调用f。 【参考方案1】:

为此使用traceback 模块:

>>> import traceback
>>> def f(depth=0):
...     print depth, traceback.print_stack()
...     if depth < 2:
...         f(depth + 1)
...
>>> f()
0  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
 None
1  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in f
  File "<stdin>", line 2, in f
 None
2  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in f
  File "<stdin>", line 4, in f
  File "<stdin>", line 2, in f
 None

因此,如果堆栈中的任何条目表明代码是从f 调用的,则该调用是(内)直接递归的。 traceback.extract_stack 方法使您可以轻松访问这些数据。下面示例中的if len(l[2] ... 语句仅计算函数名称的完全匹配数。为了让它更漂亮(感谢 agf 的想法),你可以把它变成一个装饰器:

>>> def norecurse(f):
...     def func(*args, **kwargs):
...         if len([l[2] for l in traceback.extract_stack() if l[2] == f.func_name]) > 0:
...             raise Exception, 'Recursed'
...         return f(*args, **kwargs)
...     return func
...
>>> @norecurse
... def foo(depth=0):
...     print depth
...     foo(depth + 1)
...
>>> foo()
0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in func
  File "<stdin>", line 4, in foo
  File "<stdin>", line 5, in func
Exception: Recursed

【讨论】:

【参考方案2】:

您可以使用装饰器设置的标志:

def norecurse(func):
    func.called = False
    def f(*args, **kwargs):
        if func.called:
            print "Recursion!"
            # func.called = False # if you are going to continue execution
            raise Exception
        func.called = True
        result = func(*args, **kwargs)
        func.called = False
        return result
    return f

那你就可以了

@norecurse
def f(some, arg, s):
    do_stuff()

如果 f 在运行时再次被调用,called 将是 True 并且会引发异常。

【讨论】:

这有一些不需要的内存:假设您的f 定义为:def f(func): print func()。如果你用f(f) 调用它,你会得到递归警告。如果你然后用f(lambda: 'foo') 调用它,你也会收到警告,虽然它没有递归...... 这在从多个线程调用函数的应用程序中将无法可靠地工作,尽管我猜它可能也可以通过使用 threading.local 作为布尔值的存储来适应在那里工作。 @jro 我认为这是出于调试目的,如果检测到不需要的递归,OP 不想继续执行。如果他确实想继续,可以很容易地在raiseing 之前添加func.called = False @lazyr 如果问题中没有提到,为什么我会假设代码需要是线程安全的? 我不是说你应该有。该评论旨在警告和建议在这种情况下可能使用此解决方案的人。 traceback 解决方案是线程安全的,这个不是,但可以做到。

以上是关于Python 方法是不是可以检查它是不是已从自身内部调用?的主要内容,如果未能解决你的问题,请参考以下文章

如何实现一个系统调用,它可以在不进入内核日志的情况下检查自身是不是已成功执行?

如何知道应用程序是不是已从 Google Play 或 Amazon 下载?

Python:检查范围内是不是存在对象[重复]

Python set([]) 如何检查两个对象是不是相等?一个对象需要定义哪些方法来自定义它?

检查点是不是在圆圈内

检查是不是安装了 Python 包