web-dev-qa-db-ja.com

スタックとフレームの違いは何ですか?

どのような状況で、一方を他方よりも使いたいですか?

違いは何ですか:

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

ここでのニュアンスはわかりません。

  • スタックフレーム
  • フレームオブジェクト
  • スタックトレース

update 2、質問が出されてから少し時間が経過しましたが、非常に関連性があります

46
jmunsch

さて、これは一般的にスタックフレーム/コールスタックが何であるかについてのように見えるので、これを見ていきましょう:

_def f():
    try:
        g()
    except:
        # WE WILL DO THINGS HERE

def g():
    h()

def h():
    raise Exception('stuff')

#CALL
f()
_

h()にいるとき、 呼び出しスタック には4つのフレームがあります。

_[top level]
 [f()]
  [g()]
   [h()] #<-- we're here
_

(スタックにsys.getrecursionlimit()以上のフレームを配置しようとすると、RuntimeErrorを取得します。これはStackOverflowのPythonバージョンです;-))

「外側」とは、コールスタック内の私たちの上のすべて(文字通り:「上」の方向)を指します。順番に、gf、トップ(モジュール)レベルの順になります。同様に、「内部」は、コールスタック内のすべてdownwardsを指します。 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)]
_

予想どおり、3つの内側フレームf、g、およびh。これで、最後のフレームオブジェクト(h()からのオブジェクト)を取得し、そのouterフレームを要求できます。

_[(<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と比較した順序が逆になっており、出力が減少していることに注意してください。実際、目を細めた場合、これは基本的に通常のトレースバックのように見えます(そして、ちょっと、それはisで、もう少しフォーマットがあります)。

要約:_inspect.getouterframes_と_traceback.extract_stack_の両方には、通常のトレースバックで通常見られるものを再現するためのすべての情報が含まれています。 _extract_stack_は、スタックフレームへの参照を削除するだけです。これは、スタックトレースを所定のフレームから外側にフォーマットするポイントに到達すると、それらが不要になることが非常に一般的だからです。

52
roippi

ドキュメントinspectモジュールの場合:

次の関数が「フレームレコード」を返す場合、各レコードは6つの項目のタプルです。フレームオブジェクト、ファイル名、現在の行の行番号、関数名、ソースコードからのコンテキストの行のリスト、そのリスト内の現在の行のインデックス。

ドキュメントtracebackモジュールの場合:

「前処理済み」スタックトレースエントリは4タプル(ファイル名、行番号、関数名、テキスト)です。

したがって、フレームレコードにはフレームオブジェクトとコンテキストの一部の行も含まれますが、トレースバックには呼び出しスタック内の個々の行のテキスト(つまり、extract_stack call)。

トレースバックを出力するだけの場合は、tracebackのスタックを使用します。ドキュメントが示唆するように、これはユーザーに表示するために処理される情報です。呼び出しスタックで実際に何かをする(たとえば、呼び出しフレームから変数を読み取る)には、inspectからフレームオブジェクトにアクセスする必要があります。

10
BrenBarn