堆栈和框架有啥区别?
Posted
技术标签:
【中文标题】堆栈和框架有啥区别?【英文标题】:What is the difference between a stack and a frame?堆栈和框架有什么区别? 【发布时间】:2014-07-13 22:40:02 【问题描述】:在什么情况下我想使用一种而不是另一种?
有什么区别:
>>> import inspect
>>> print(inspect.getouterframes(inspect.currentframe()))
[(<frame object at 0x8fc262c>, '<stdin>', 1, '<module>', None, None)]
还有:
>>> import traceback
>>> traceback.extract_stack()
[('<stdin>', 1, '<module>', None)]
更新:
另一个:
>>> import sys
>>> print(sys._getframe().f_trace,sys._getframe().f_code)
(None, <code object <module> at 0x8682a88, file "<stdin>", line 1>)
我不明白这里的细微差别:
堆栈帧 框架对象 堆栈跟踪更新 2,距离提出问题有一点时间,但非常相关
Explain the concept of a stack frame in a nutshell【问题讨论】:
@BrenBarn 这显然不是唯一的区别,因为getouterframes
包含更多数据。
请注意,虽然差异似乎微不足道,但traceback.extract_stack()
不包含对堆栈帧的引用这一事实非常重要。您对框架对象的每个引用都是内存泄漏(因为该框架引用的任何内容现在都不符合 gc 条件),因此在长时间运行的程序中这样做是一个很大的禁忌。
@roippi 不,不一定是内存泄漏。如果您不在局部变量中保留对框架对象的引用,那么您甚至没有引用循环。如果你确实有一个参考循环,你可以明确地打破它。即使您不理会引用循环,如果可以从该循环访问具有__del__
方法的任何内容并且您没有运行Python 3.4 或更高版本(请参阅PEP 442),这也只是内存泄漏.这些复杂的条件使得正确使用它棘手,但并非不可能,即使在一次运行数周的服务器中也是如此。
@delnan 我并不是在暗示 gc 无法检测到循环引用。我的意思是,如果您坚持那些框架对象引用(例如,***全局错误处理程序或其他东西),就会发生不好的事情。
@roippi 好吧,这只是一种可怕的说法,即“很多东西都可以从框架中获取”。这与任何其他参考文献没有根本不同。
【参考方案1】:
好吧,因为这似乎更多的是关于堆栈帧/调用堆栈的一般情况,让我们来看看:
def f():
try:
g()
except:
# WE WILL DO THINGS HERE
def g():
h()
def h():
raise Exception('stuff')
#CALL
f()
当我们在h()
时,call stack 上有 4 帧。
[top level]
[f()]
[g()]
[h()] #<-- we're here
(如果我们尝试在堆栈中放入超过sys.getrecursionlimit()
的帧,我们将得到RuntimeError
,这是python 版本的***
;-))
“外部”指的是调用堆栈中我们上方的所有内容(字面意思是“向上”方向)。所以按照顺序,g
,然后是f
,然后是顶层(模块)级别。同样,“内部”指的是调用堆栈中向下的所有内容。如果我们在f()
中捕获异常,则该回溯对象将引用所有内部堆栈帧,这些帧已展开以使我们到达该点。
def f():
try:
g()
except:
import inspect
import sys
#the third(last) item in sys.exc_info() is the current traceback object
return inspect.getinnerframes(sys.exc_info()[-1])
这给出了:
[(<frame object at 0xaad758>, 'test.py', 3, 'f', [' g()\n'], 0),
(<frame object at 0x7f5edeb23648>, 'test.py', 10, 'g', [' h()\n'], 0),
(<frame object at 0x7f5edeabdc50>, 'test.py', 13, 'h', [" raise Exception('stuff')\n"], 0)]
正如所料,三个内框 f、g 和 h。现在,我们可以获取最后一个框架对象(来自h()
的那个)并请求它的外部 框架:
[(<frame object at 0x7f6e996e6a48>, 'test.py', 13, 'h', [" raise Exception('stuff')\n"], 0),
(<frame object at 0x1bf58b8>, 'test.py', 10, 'g', [' h()\n'], 0),
(<frame object at 0x7f6e99620240>, 'test.py', 7, 'f', [' return inspect.getinnerframes(sys.exc_info()[-1])\n'], 0),
(<frame object at 0x7f6e99725438>, 'test.py', 23, '<module>', ['print(inspect.getouterframes(f()[-1][0]))\n'], 0)]
所以,你去吧,这就是正在发生的一切:我们只是在调用堆栈中导航。为了比较,下面是traceback.extract_stack(f()[-1][0])
给出的:
[('test.py', 23, '<module>', 'print(traceback.extract_stack(f()[-1][0]))'),
('test.py', 7, 'f', 'return inspect.getinnerframes(sys.exc_info()[-1])'),
('test.py', 10, 'g', 'h()'),
('test.py', 13, 'h', "raise Exception('stuff')")]
请注意这里与getouterframes
相比的倒序,以及减少的输出。事实上,如果你眯着眼睛,这基本上看起来就像一个常规的回溯(嘿,它是,只是格式多了一点)。
总结:inspect.getouterframes
和traceback.extract_stack
都包含重现您在日常回溯中通常看到的所有信息; extract_stack
只是删除了对堆栈帧的引用,因为一旦您从给定的帧向外格式化堆栈跟踪,就不再需要它们是很常见的。
【讨论】:
【参考方案2】:The documentation inspect
模块说:
当以下函数返回“帧记录”时,每条记录是六项的元组:帧对象、文件名、当前行的行号、函数名、源上下文的行列表代码,以及该列表中当前行的索引。
The documentation traceback
模块说:
“预处理”堆栈跟踪条目是一个 4 元组(文件名、行号、函数名、文本)
因此区别在于框架记录还包括框架对象和一些上下文行,而回溯只包括调用堆栈中各个行的文本(即导致extract_stack
调用的调用)。
如果您只想打印回溯,您可以使用来自traceback
的堆栈。正如文档所暗示的,这是为向用户显示而处理的信息。如果您想对调用堆栈实际执行任何操作(例如,从调用帧中读取变量),则需要从 inspect
访问框架对象。
【讨论】:
以上是关于堆栈和框架有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章