Как заметил Барни , комментарий , который гласит:
# next bit filched from 1.5.2's inspect.py
, использует слово для обозначения «скопировано из».То есть эта конкретная кодовая последовательность существует уже очень давно, начиная с версии Python 1.5.2.
Что здесь происходит (правка: эта часть вопроса была отредактирована!) просто но тонко.Любое исключение заставляет систему Python находить самый внутренний, в настоящее время активный обработчик except
.В данном случае это самая следующая строка - так:
try:
raise Exception
except:
...
переходит непосредственно к строке ...
.Тем не менее, у raise
есть побочный эффект , который является ключом ко всему.Побочным эффектом является то, что raise
заставляет стек трассировки содержать в качестве самой последней записи 1 состояние выполнения, указывающее на саму строку raise
.
Функция sys.exc_info()
возвращает кортеж с тремя элементами: тип исключения, значение исключения - здесь не было передано никакого значения, потому что обработчик не нуждается в нем, - и (весь) стек трассировки.[2]
извлекает этот стек трассировки из кортежа, отбрасывая тип и значение исключения.
Структура стека трассировки несколько сложна, но в каждом экземпляре стека трассировки есть атрибут .tb_frame
.Он содержит информацию о кадре стека, который был активен при возникновении исключения.Поскольку это стек активаций функций, его предшественником является состояние, которое было активным при вызове currentframe
, поэтому это кадр вызывающего абонента.
Этот метод определения местоположения кадра вызывающего абонента не очень эффективен (и, как holdenweb указывает , специфично для интерпретатора CPython), поэтому, если sys
имеет функцию _getframe
, файл повторно связывается currentframe
, чтобы вызвать sys._getframe(3)
.(Я не уверен, что здесь делает константа 3, поскольку другая версия эффективно возвращает то, что вернуло бы sys._getframe(0)
. Правка 2: при дальнейшей проверке магическая константа 3 заботится о том, чтобы обработчик журнала вызывал _log
, который вызывает findCaller
, который вызывает currentframe
. Это еще один хак эффективности, так как findCaller
поднимается по каждому фрейму стека в поисках того, что встречается в каком-то файле , отличном от , самого кода модуля протоколирования.Лучшее замечание.)
1 Помните, что стек - это любая структура данных, которая ведет себя по принципу «последний пришел - первым вышел» (LIFO).Интерпретатор Python управляет множеством различных, но более или менее одновременных стеков, включая обработчики исключений и обычный механизм вызова функций.