[英]How do I get the current depth of the Python interpreter stack?
从文档:
sys.getrecursionlimit()
返回递归限制的当前值,Python解释器堆栈的最大深度。 此限制可防止无限递归导致 C 堆栈溢出和 Python 崩溃。 可以通过 setrecursionlimit() 设置。
我目前在酸洗对象时达到了递归限制。 我正在酸洗的对象只有几层嵌套,所以我对发生的事情感到有些困惑。
我已经能够通过以下黑客来规避这个问题:
try:
return pickle.dumps(x)
except:
try:
recursionlimit = getrecursionlimit()
setrecursionlimit(2*recursionlimit)
dumped = pickle.dumps(x)
setrecursionlimit(recursionlimit)
return dumped
except:
raise
在不同的上下文中测试上述代码片段有时会导致第一次try
成功,有时会导致第二次try
成功。 到目前为止,我还没有能够让它raise
异常。
为了进一步调试我的问题,有一种方法可以获得堆栈的当前深度会很有帮助。 这将允许我验证输入堆栈深度是否决定了上面的代码片段在第一次try
还是第二次try
会成功。
标准库是否提供了获取栈深度的函数,如果没有,如何获取?
def get_stack_depth():
# what goes here?
您可以从inspect.stack()
看到整个调用堆栈,因此当前采用的深度将是len(inspect.stack(0))
。
另一方面,我猜您在引发“超出最大递归深度”异常时打印了完整的堆栈。 该堆栈跟踪应准确显示出了什么问题。
如果速度是一个问题,绕过检查模块会更快。
testing depth: 50 (CPython 3.7.3)
stacksize4b() | depth: 50 | 2.0 µs
stacksize4b(200) | depth: 50 | 2.2 µs
stacksize3a() | depth: 50 | 2.4 µs
stacksize2a() | depth: 50 | 2.9 µs
stackdepth2() | depth: 50 | 3.0 µs
stackdepth1() | depth: 50 | 3.0 µs
stackdepth3() | depth: 50 | 3.4 µs
stacksize1() | depth: 50 | 7.4 µs # deprecated
len(inspect.stack()) | depth: 50 | 1.9 ms
我将我的函数名称缩短为stacksize()
并且为了更容易区分,我将@lunixbochs 的函数称为stackdepth()
。
对于小堆栈大小,这可能是代码简洁性、可读性和速度之间的最佳折衷。 对于不到 10 帧,由于开销较低,只有stackdepth1()
稍微快一些。
from itertools import count
def stack_size2a(size=2):
"""Get stack size for caller's frame.
"""
frame = sys._getframe(size)
for size in count(size):
frame = frame.f_back
if not frame:
return size
为了为更大的堆栈大小实现更好的时序,一些更精细的算法是可能的。 stacksize3a()
将链式属性查找与来自stackdepth1()
的近距离完成相结合,以获得更有利的时序斜率,在我的基准测试中开始获得大约 > 70 帧的回报。
from itertools import count
def stack_size3a(size=2):
"""Get stack size for caller's frame.
"""
frame = sys._getframe(size)
try:
for size in count(size, 8):
frame = frame.f_back.f_back.f_back.f_back.\
f_back.f_back.f_back.f_back
except AttributeError:
while frame:
frame = frame.f_back
size += 1
return size - 1
正如@lunixbochs 在回答中提出的那样, sys._getframe()
基本上是 C 代码中的stackdepth1()
。 虽然更简单的算法总是从堆栈顶部的现有帧开始在 Python 中的深度搜索,向下检查堆栈以获取更多现有帧,但stacksize4b()
允许通过其stack_hint
参数从任何级别开始搜索,并且可以搜索堆栈如果需要,向下或向上。
在sys._getframe()
,调用sys._getframe()
始终意味着将堆栈从顶部帧向下移动到指定深度。 因为 Python 和 C 之间的性能差异如此巨大,如果有必要,在应用基本的近距离逐帧之前,多次调用sys._getframe()
仍然可以得到回报使用frame.f_back
在 Python 中搜索。
from itertools import count
def stack_size4b(size_hint=8):
"""Get stack size for caller's frame.
"""
get_frame = sys._getframe
frame = None
try:
while True:
frame = get_frame(size_hint)
size_hint *= 2
except ValueError:
if frame:
size_hint //= 2
else:
while not frame:
size_hint = max(2, size_hint // 2)
try:
frame = get_frame(size_hint)
except ValueError:
continue
for size in count(size_hint):
frame = frame.f_back
if not frame:
return size
stacksize4b()
的使用思路是将大小提示置于预期堆栈深度的下限,以便快速启动,同时仍然能够应对堆栈深度的每一次剧烈和短暂的变化。
基准测试显示stacksize4b()
与默认size_hint=8
和调整size_hint=200
。 对于基准测试,3-3000 范围内的所有堆栈深度都经过测试,以显示stacksize4b()
时序中的特征锯齿图案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.