如何获取Python解释器堆栈的当前深度?

如何获取Python解释器堆栈的当前深度?,python,stack,interpreter,Python,Stack,Interpreter,从: sys.getrecursionlimit() 返回递归限制的当前值,即Python解释器堆栈的最大深度。此限制可防止无限递归 导致C堆栈溢出和Python崩溃。可能是 由setrecursionlimit()设置 我当前在酸洗对象时达到了递归限制。我正在酸洗的对象只有几层嵌套,所以我对正在发生的事情有点困惑 我已经能够通过以下方法规避该问题: try: return pickle.dumps(x) except: try: recursionlimit =

从:

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
中成功。到目前为止,我还不能将其作为例外

为了进一步调试我的问题,有一种获取堆栈当前深度的方法会很有帮助。这将允许我验证输入的堆栈深度是否决定上面的代码段在第一次
try
还是第二次成功

标准库是否提供了获取堆栈深度的函数?如果没有,如何获取

def get_stack_depth():
    # what goes here?

您可以从
inspect.stack()
中看到整个调用堆栈,因此当前获取的深度将是
len(inspect.stack(0))


另一方面,我猜当引发“超过最大递归深度”异常时,您得到了完整的堆栈。堆栈跟踪应该准确地显示出出错的地方。

如果速度是一个问题,那么绕过inspect模块的速度要快得多

测试深度:50(CPython 3.7.3)
stacksize4b()|深度:50 | 2.0µs
堆叠尺寸4B(200)|深度:50 | 2.2µs
stacksize3a()|深度:50 | 2.4µs
stacksize2a()|深度:50 | 2.9µs
堆栈深度2()|深度:50 | 3.0µs
堆栈深度1()|深度:50 | 3.0µs
堆栈深度3()|深度:50 | 3.4µs
stacksize1()|深度:50 | 7.4µs#已弃用
透镜(inspect.stack())|深度:50 | 1.9毫秒
我将函数名缩短为
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.\u getframe()
基本上是C代码中的
stackdepth1()。在Python中,较简单的算法总是从堆栈顶部的现有帧开始深度搜索,向下检查堆栈中是否有更多的现有帧,
stacksize4b()
允许通过其
stack\u hint
-参数从任何级别开始搜索,如果需要,可以向下或向上搜索堆栈

在引擎盖下,调用sys.\u getframe()
总是意味着从顶部框架向下移动堆栈到指定深度。由于Python和C之间的性能差异如此巨大,因此在使用
frame.f\u back
在Python中应用基本的近距离逐帧搜索之前,如果需要多次调用
sys.\u getframe()
以查找更接近最深的帧,仍然可以获得回报

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\u-hint=8
,调整后的
size\u-hint=200
。对于基准测试,测试范围为3-3000的所有堆栈深度,以显示
stacksize4b()
定时的特征saw模式


这听起来像是XY问题。与其弄清楚如何绕过递归限制,不如试着弄清楚为什么会碰到它。@i确实是这样。这正是我想做的,我想看看问题是否取决于我发出违规调用时的堆栈深度。要做到这一点,我需要弄清楚如何获得堆栈的当前深度。如果您能够提供在get/set限制之间所做的事情,那会更好……至少是一个原型版本。这样我们就可以破解它了。
x
的值是多少?谢谢,
len(inspect.stack())
正是我想要的。@lunixbochs该算法的使用思想是将大小提示放在跳转开始时预期堆栈深度的下限,同时仍然能够应对堆栈深度的每一次剧烈和短暂的变化。下限而非中间值,因为触发的异常相对昂贵,因此,算法的反向搜索应该是有限的。@lunixbochs如果你有一个更大的应用程序,它基本上不会低于某个堆栈深度,并且你有一个很好的理由首先考虑速度而不是代码的简洁性,就像你经常尝试记录调用者一样(Python的动态特性使得这对于静态方法、堆栈深度本身或类似的东西来说非常困难。@lunixbochs是的,听起来是个原因。这取决于预期的堆栈大小。如果平均不超过20帧,你应该可以坚持你的答案