Подтвердить что ты не робот

В чем разница между стеком и фреймом?

В каких ситуациях я хотел бы использовать один над другим?

В чем разница между:

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

Update:

Другой:

>>> import sys
>>> print(sys._getframe().f_trace,sys._getframe().f_code)
(None, <code object <module> at 0x8682a88, file "<stdin>", line 1>)

Я не понимаю здесь нюансов:

  • Стековая рамка
  • Объект кадра
  • Трассировка стека
4b9b3361

Ответ 1

Хорошо, поскольку это, по-видимому, больше о том, какие стековые фреймы/стеки вызовов в общем случае позволяют пройти через это:

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

def g():
    h()

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

#CALL
f()

Когда мы находимся в h(), в 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

Документация для модуля inspect гласит:

Когда следующие функции возвращают "записи кадров", каждая запись представляет собой набор из шести элементов: объект фрейма, имя файла, номер строки текущей строки, имя функции, список строк контекста из источника кода и индекса текущей строки в этом списке.

Документация для модуля traceback гласит:

"Предварительно обработанная" запись трассировки стека представляет собой 4-кортеж (имя файла, номер строки, имя функции, текст)

Следовательно, разница состоит в том, что запись кадра также включает в себя объект кадра и некоторые строки контекста, тогда как трассировка включает только текст отдельных строк в стеке вызовов (т.е. вызовы, которые привели к вызову extract_stack).

Вы использовали бы стек из traceback, если хотите распечатать трассировку. Как предполагает документация, это информация обрабатывается для показа пользователю. Вам понадобится доступ к объекту фрейма от inspect, если вы действительно хотите что-либо сделать с помощью стека вызовов (например, читать переменные из вызывающих фреймов).