Python 方法是不是可以检查它是不是已从自身内部调用?
Posted
技术标签:
【中文标题】Python 方法是不是可以检查它是不是已从自身内部调用?【英文标题】:Can a Python method check if it has been called from within itself?Python 方法是否可以检查它是否已从自身内部调用? 【发布时间】:2011-12-15 13:36:07 【问题描述】:假设我有一个 Python 函数 f
和 fhelp
。 fhelp
旨在递归调用自身。 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 不想继续执行。如果他确实想继续,可以很容易地在raise
ing 之前添加func.called = False
。
@lazyr 如果问题中没有提到,为什么我会假设代码需要是线程安全的?
我不是说你应该有。该评论旨在警告和建议在这种情况下可能使用此解决方案的人。 traceback
解决方案是线程安全的,这个不是,但可以做到。以上是关于Python 方法是不是可以检查它是不是已从自身内部调用?的主要内容,如果未能解决你的问题,请参考以下文章
如何实现一个系统调用,它可以在不进入内核日志的情况下检查自身是不是已成功执行?
如何知道应用程序是不是已从 Google Play 或 Amazon 下载?