[英]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.