堆栈和框架有什么区别?

时间:2014-05-24 18:32:02

标签: python inspect traceback sys

在什么情况下我想用一个而不是另一个?

有什么区别:

>>> 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>)

我不明白这里的细微差别:

  • Stack Frame
  • 框架对象
  • Stack Trace

更新2,问题问题后的一段时间,但非常相关

2 个答案:

答案 0 :(得分:53)

好吧,因为这似乎更多地是关于堆栈帧/调用堆栈的一般情况,让我们来看看:

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,这是StackOverflow的python版本; - ))< / p>

&#34;外&#34;指的是调用堆栈中我们上面的所有内容(字面意思:方向&#34; up&#34;)。那么按顺序,g,然后是f,然后是顶级(模块)级别。同样地,&#34;内部&#34;指的是调用堆栈中的所有向下。如果我们在f()中捕获异常,那个traceback对象将引用所有已展开的内部堆栈帧以使我们到达那一点。

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.getouterframestraceback.extract_stack都包含所有信息,以重现您在日常追溯中通常看到的内容; extract_stack只是删除了对堆栈帧的引用,因为一旦你从一个给定的帧向外格式化堆栈跟踪,它就不再需要它们了。

答案 1 :(得分:10)

inspect模块的

The documentation说:

  

当以下函数返回“帧记录”时,每个记录是六个项目的元组:框架对象,文件名,当前行的行号,函数名称,来自源的上下文行列表代码,以及该列表中当前行的索引。

traceback模块的

The documentation说:

  

A&#34;预处理&#34;堆栈跟踪条目是一个4元组(文件名,行号,函数名,文本)

因此,不同之处在于帧记录还包括帧对象和一些上下文行,而回溯仅包括调用堆栈中各行的文本(即导致{{1}的调用呼叫)。

如果您只想打印回溯,可以使用extract_stack中的堆栈。如文档所示,这是为了向用户显示而处理的信息。如果您想对调用堆栈实际执行任何操作(例如,从调用帧读取变量),则需要从traceback访问框架对象。