如果速度是个问题,绕过检查模块会更快。
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() 允许从任何级别通过其 @ 开始搜索987654338@-parameter 并且可以根据需要向下或向上搜索堆栈。
在底层,调用sys._getframe() 总是意味着将堆栈从顶部框架向下移动到指定深度。由于 Python 和 C 之间的性能差异如此之大,因此在应用基本的近距离逐帧搜索之前,如果需要找到更接近最深的帧,仍然可以多次调用 sys._getframe() Python 与 frame.f_back.
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() 时序中的特征锯齿图案。