Wiem, że jeśli chcę ponownie zgłosić wyjątek, po prostu używam raise
bez argumentów w odpowiednim except
bloku. Ale biorąc pod uwagę zagnieżdżone wyrażenie, takie jak
try:
something()
except SomeError as e:
try:
plan_B()
except AlsoFailsError:
raise e # I'd like to raise the SomeError as if plan_B()
# didn't raise the AlsoFailsError
jak mogę ponownie podbić SomeError
bez przerywania śledzenia stosu? raise
sam w tym przypadku ponownie podniósłby nowsze AlsoFailsError
. Albo jak mogę zmienić kod, aby uniknąć tego problemu?
plan_B
inną funkcję, która zwracaTrue
po sukcesie iFalse
po wyjątku? Wtedy zewnętrznyexcept
blok mógłby być po prostuif not try_plan_B(): raise
arg
i spróbuję zadzwonić,arg.plan_B()
co możeAttributeError
spowodować podniesienie z powoduarg
braku planu Bplan_B
na zgłaszanie wyjątkówOdpowiedzi:
Począwszy od Pythona 3, śledzenie jest przechowywane w wyjątku, więc prostota
raise e
zrobi (głównie) właściwą rzecz:Utworzone dane śledzenia będą zawierały dodatkowe powiadomienie, które
SomeError
wystąpiło podczas obsługiAlsoFailsError
(ponieważraise e
znajduje się w środkuexcept AlsoFailsError
). Jest to mylące, ponieważ to, co faktycznie się stało, jest odwrotne - napotkaliśmyAlsoFailsError
i poradziliśmy sobie z tym, próbując odzyskać siłySomeError
. Aby uzyskać traceback, który nie zawieraAlsoFailsError
wymienićraise e
zraise e from None
.W Pythonie 2 zapisujesz typ wyjątku, wartość i dane śledzenia w zmiennych lokalnych i używasz trzyargumentowej formy
raise
:źródło
raise self.exc_info[1], None, self.exc_info[2]
poself.exc_info = sys.exc_info()
- postawieniu[1]
na pierwszym miejscu z jakiegoś powoduraise t, None, tb
utraci wartość wyjątku i wymusiraise
ponowne utworzenie jego wystąpienia z typu, podając mniej konkretną (lub po prostu niepoprawną) wartość wyjątku. Na przykład, jeśli podniesiony wyjątek toKeyError("some-key")
, po prostu podniesie ponownieKeyError()
i pominie dokładny brakujący klucz z śledzenia.raise v.with_traceback(tb)
. (Twój komentarz mówi nawet to samo, z wyjątkiem tego, że proponuje ponowne utworzenie instancji wartości.)sys.exc_info()
w zmiennej lokalnej, miało sens przed wersją Python 2.0 (wydaną 13 lat temu), ale dziś graniczy z absurdem. Nowoczesny Python byłby prawie bezużyteczny bez kolektora cykli, ponieważ każda nietrywialna biblioteka Pythona tworzy cykle bez przerwy i zależy od ich prawidłowego czyszczenia.Nawet jeśli przyjęte rozwiązanie jest słuszne, dobrze jest wskazać bibliotekę Six, która ma rozwiązanie Python 2 + 3, używając
six.reraise
.Możesz więc napisać:
źródło
six.raise_from
jeśli chcesz dołączyć informacje, któreplan_B()
również zawiodły.six.raise_from
tworząc nowy wyjątek, który jest powiązany z poprzednim, nie przebijasz ponownie , więc śledzenie wstecz jest inne.reraise
wrażenie tylkosomething()
rzuconeSomeError
, jeśliraise_from
wiesz również, że spowodowałoplan_B()
to wykonanie, ale rzucanieAlsoFailsError
. Więc to zależy od zastosowania. Myślę, żeraise_from
ułatwi to debugowanieZgodnie z sugestią Drew McGowena , ale biorąc pod uwagę ogólny przypadek (gdzie
s
obecna jest wartość zwracana ), oto alternatywa dla odpowiedzi użytkownika4815162342 :źródło
raise from
, więc śledzenie stosu pozwoliłoby mi również na niepowodzenie planu B. Które można przy okazji emulować w Pythonie 2 .Python 3.5+ i tak dołącza informacje śledzenia do błędu, więc nie jest już konieczne zapisywanie ich osobno.
źródło
except
. Ale masz rację, kiedy zastępujęerr = e
, powiedzmy,raise AttributeError
pierwszySyntaxError
ślad stosu, a następnie aDuring handling of the above exception, another exception occurred:
iAttributeError
ślad stosu. Dobrze wiedzieć, ale niestety nie można polegać na zainstalowaniu 3.5+. PS: ff verstehen nicht-Deutsche vermutlich nicht;)