2009-11-01 11 views
11

Widziałem w różnych bazach kodu i po prostu czytałem na PyMOTW (patrz pierwsza uwaga here).Dlaczego istnieje potrzeba jawnego usunięcia śledzenia sys.exc_info()?

Wyjaśnienie mówi, że cykl zostanie utworzony w przypadku, gdy traceback zostanie przypisany do zmiennej z sys.exc_info()[2], ale dlaczego tak jest?

Jak duży jest problem? Czy powinienem wyszukać wszystkie zastosowania exc_info w mojej bazie kodu i upewnić się, że traceback został usunięty?

Odpowiedz

19

Python 3 (aktualizacja do oryginalnego odpowiedź):

W Pythonie 3, rada cytowane w pytaniu została usunięta z dokumentacją Pythona. Moja oryginalna odpowiedź (poniżej) dotyczy tylko wersji Pythona zawierających cytat z ich dokumentacji.

Python 2:

Python garbage collector będzie w końcu znaleźć i usunąć koliste referencje, jak ten stworzony przez odniesienie do stosu Traceback od wewnątrz jednego stosu ramki siebie, więc nie idź wróć i przepisz swój kod. Ale idąc dalej, można stosować się do zaleceń

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

(gdzie udokumentowano exc_info()) i powiedzieć:

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

gdy trzeba chwycić wyjątek.

Dwa kolejne myśli:

Po pierwsze, dlaczego uciekasz exc_info() w ogóle?

Jeśli chcesz złapać wyjątek nie powinien po prostu powiedzieć:

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

zamiast mucking z obiektami wewnątrz modułu sys?

Po drugie: Dobra, dałem wiele rad, ale tak naprawdę nie odpowiedziałem na twoje pytanie.:-)

Dlaczego tworzony jest cykl? Cóż, w prostych przypadkach, cykl jest tworzony, gdy obiekt odnosi się do siebie:

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

Albo gdy dwa obiekty odnoszą się do siebie nawzajem:

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

W obu tych przypadkach, gdy końce funkcyjnych wartości zmiennych będą nadal istnieć, ponieważ są zablokowane w uścisku z odniesieniem: żadne nie może zniknąć, dopóki drugie nie zniknie! Tylko nowoczesny garbage Python może rozwiązać ten problem, zauważając w końcu pętlę i ją łamiąc.

Tak więc kluczem do zrozumienia tej sytuacji jest obiekt "traceback" - trzecia rzecz zwrócona przez exc_info() - zawiera "ramkę stosu" dla każdej funkcji, która była aktywna po wywołaniu wyjątku . I te ramki stosów są "martwymi" obiektami pokazującymi, co jest prawdziwe, gdy wywołano execption; ramy są wciąż żywe! Funkcja, która wychwyciła wyjątek, wciąż jest żywa, więc jego ramka stosu jest żywą istotą, wciąż rosnącą i tracącą zmienne odniesienia, ponieważ jej kod jest wykonywany w celu obsługi wyjątku (i robienia wszystkiego, co robi, gdy kończy się klauzula "wyjątkiem" i przechodzi o swojej pracy).

Kiedy więc powiemy t = sys.exc_info()[2], jedna z tych ramek wewnątrz traceback - w rzeczywistości rama należąca do aktualnie działającej funkcji - teraz ma zmienną o nazwie t, która wskazuje na ramkę stosu samodzielnie, tworząc pętlę, podobnie jak te, które pokazałem powyżej.

+1

[Ostrzeżenie] (https://docs.python.org/3.2/library/sys.html#sys.exc_info), o którym wspomniałeś, używając porady "[: 2]", zostało usunięte z dokumentów, ponieważ Python 3.3. Powody są wyjaśnione w [# 7340] (https://bugs.python.org/issue7340). Sztuczka '[: 2]' już nie działa, atrybut '__traceback__' obsługiwanego wyjątku powinien być zamiast tego ustawiony na' Brak'. Pokrewne PEP: [344] (https://www.python.org/dev/peps/pep-0344/#open-issue-garbage-collection), [3134] (https://www.python.org/dev/peps/pep-3134/# open-issue-garbage-collection), [3110] (https://www.python.org/dev/peps/pep-3110/#semantic-changes). – Delgan

10

Funkcja traceback zawiera odniesienia do wszystkich aktywnych ramek, które z kolei zawierają odniesienia do wszystkich zmiennych lokalnych w tych różnych ramkach - te odniesienia są dużą częścią pracy obiektów traceback i frame, więc trudno się dziwić . Tak więc, jeśli dodasz odwołanie do traceback (lub nie usuniesz go natychmiastowo po tymczasowym dodaniu go), nieuchronnie utworzysz dużą pętlę referencji - która przeszkadza w zbieraniu śmieci (i może zatrzymać ją całkowicie, jeśli którykolwiek z obiektów w pętli należą do klas, które ponad __del__, metoda finalizatora).

Szczególnie w przypadku długotrwałego programu, ingerencja w zbieranie śmieci nie jest najlepszym pomysłem, ponieważ będziesz trzymać się pamięci, której naprawdę nie potrzebujesz (dłużej niż to konieczne lub na czas nieokreślony, jeśli masz zasadniczo zablokował zbieranie śmieci na takich pętlach przez umieszczenie ich w obiektach zawierających finalizatory).

Dlatego zdecydowanie najlepiej pozbyć się śladów jak najszybciej, bez względu na to, czy pochodzą one z exc_info, czy nie!

Powiązane problemy