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

Почему необходимо явно удалить трассировку sys.exc_info()?

Я видел в разных базовых кодах и просто читал на PyMOTW (см. первое примечание здесь).

Объяснение гласит, что цикл будет создан, если трассировка будет назначена переменной из sys.exc_info()[2], но почему это?

Насколько велика проблема? Должен ли я искать все виды использования exc_info в моей базе кода и убедиться, что трассировка удалена?

4b9b3361

Ответ 1

Python 3 (обновление до исходного ответа):

В Python 3 совет, цитируемый в вопросе, был удален из документации Python. Мой первоначальный ответ (который следует) применяется только к версиям Python, которые включают цитату в их документации.

Python 2:

Сборщик мусора Python, в конце концов, найдет и удалит круговые ссылки, подобные тому, который создан путем ссылки на стек трассировки изнутри одного из самих стековых фреймов, поэтому не возвращайтесь и не переписывайте свой код. Но, продвигаясь вперед, вы можете следовать советам

http://docs.python.org/library/sys.html

(где он документирует exc_info()) и скажет:

exctype, value = sys.exc_info()[:2]

когда вам нужно получить исключение.

Еще две мысли:

Во-первых, почему вы используете exc_info() вообще?

Если вы хотите поймать исключение, вы не должны просто сказать:

try:
    ...
except Exception as e:  # or "Exception, e" in old Pythons
    ... do with with e ...

вместо того, чтобы обманывать объекты внутри модуля sys?

Вторые: Хорошо, я дал много советов, но на самом деле не ответил на ваш вопрос.: -)

Почему создается цикл? Ну, в простых случаях цикл создается, когда объект ссылается на себя:

a = [1,2,3]
a.append(a)

Или, когда два объекта относятся друг к другу:

a = [1,2,3]
b = [4,5,a]
a.append(b)

В обоих случаях, когда функция заканчивается, значения переменных все равно будут существовать, потому что они заблокированы в объявлении отсчета: ни один не может уйти, пока другой не уйдет первым! Только современный сборщик мусора Python может разрешить это, со временем заметив цикл и сломав его.

И поэтому ключом к пониманию этой ситуации является то, что объект "трассировка" - третья вещь (по индексу № 2), возвращаемая exc_info() - содержит "фрейм стека" для каждой функции, которая была активна, когда исключение было называется. И эти фреймы стека не являются "мертвыми" объектами, показывающими, что было истинным при вызове execption; кадры все еще живы! Функция, которая поймала исключение, все еще жива, поэтому ее стек кадров является живым существом, все еще растущим и теряющим ссылки на переменные, поскольку его код выполняет обработку исключения (и делает все, что он делает по мере завершения предложения "except", и идет о его работе).

Итак, когда вы говорите t = sys.exc_info()[2], один из этих кадров стека внутри трассировки - кадр, фактически принадлежащий самой функции, которая в настоящее время работает, теперь имеет переменную в ней с именем t, которая указывает на сам кадр стека, создавая цикл, подобный тем, которые я показал выше.

Ответ 2

Отслеживание содержит ссылки на все активные кадры, которые, в свою очередь, содержат ссылки на все локальные переменные в этих различных кадрах - эти ссылки являются большой частью самой работы объектов трассировки и кадра, поэтому это не удивительно. Таким образом, если вы добавите ссылку обратно в трассировку (или не сможете быстро ее удалить), вы неизбежно сформируете большой цикл ссылок, который мешает сборке мусора (и может полностью остановить его, если какой-либо из объектов в цикле принадлежат классам, которые переопределяют __del__, метод финализатора).

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

Итак, лучше всего избавиться от трассбеков как можно скорее, независимо от того, пришли они из exc_info или нет!