python: Skąd mam wiedzieć, jaki wystąpił wyjątek?


230

Mam funkcję wywoływaną przez program główny:

try:
    someFunction()
except:
    print "exception happened!"

ale w trakcie wykonywania funkcji podnosi wyjątek, więc przeskakuje do exceptczęści.

Jak mogę zobaczyć dokładnie, co się stało w tym, someFunction()co spowodowało wyjątek?


9
Nigdy nie używaj goły except:(bez goły raise), z wyjątkiem może raz na program, a najlepiej nie wtedy.
Mike Graham

Jeśli użyjesz wielu exceptklauzul, nie będziesz musiał sprawdzać typu wyjątku, co zwykle robi się, aby działać zgodnie z określonym typem wyjątku.
Rik Poggi

3
Jeśli zależy ci na typie wyjątku, dzieje się tak dlatego, że rozważałeś już, jakie typy wyjątku mogą logicznie wystąpić.
Karl Knechtel

3
Wewnątrz exceptbloku wyjątek jest dostępny poprzez sys.exc_info()funkcję - ta funkcja zwraca krotkę trzech wartości, które dają informacje o aktualnie obsługiwanym wyjątku.
Piotr Dobrogost,

Odpowiedzi:


384

Wszystkie pozostałe odpowiedzi wskazują, że nie należy wychwytywać ogólnych wyjątków, ale wydaje się, że nikt nie chce ci powiedzieć, dlaczego, co jest niezbędne do zrozumienia, kiedy możesz złamać „zasadę”. Oto wyjaśnienie. Zasadniczo jest tak, że nie ukrywasz:

Tak długo, jak dbasz o to, aby nie zrobić żadnej z tych rzeczy, możesz uchwycić ogólny wyjątek. Na przykład możesz podać użytkownikowi informacje o wyjątku w inny sposób, na przykład:

  • Prezentuj wyjątki jako okna dialogowe w GUI
  • Przenieś wyjątki z wątku lub procesu roboczego do wątku lub procesu sterującego w aplikacji wielowątkowej lub wieloprocesowej

Jak więc złapać ogólny wyjątek? Istnieje kilka sposobów. Jeśli chcesz tylko obiekt wyjątku, zrób to w następujący sposób:

try:
    someFunction()
except Exception as ex:
    template = "An exception of type {0} occurred. Arguments:\n{1!r}"
    message = template.format(type(ex).__name__, ex.args)
    print message

Zrobić pewny message jest zwrócenie uwagi użytkownika na dysku-do-miss sposób! Wydruk, jak pokazano powyżej, może nie wystarczyć, jeśli wiadomość jest zakopana w wielu innych wiadomościach. Brak zwrócenia uwagi użytkowników jest równoznaczny z połknięciem wszystkich wyjątków, a jeśli jest jedno wrażenie, które powinieneś odejść po przeczytaniu odpowiedzi na tej stronie, oznacza to, że nie jest to dobra rzecz . Zakończenie bloku wyjątków raiseinstrukcją rozwiąże problem poprzez przejrzyste powtórzenie wychwyconego wyjątku.

Różnica między powyższym a używaniem except:bez żadnego argumentu jest dwojaka:

  • Naga except:nie daje obiektu wyjątku do sprawdzenia
  • Wyjątki SystemExit, KeyboardInterrupta GeneratorExitnie są objęte powyższym kodzie, który jest zwykle to, co chcesz. Zobacz hierarchię wyjątków .

Jeśli chcesz również uzyskać taki sam ślad stosu, jeśli nie złapiesz wyjątku, możesz uzyskać to w ten sposób (nadal wewnątrz klauzuli wyjątku):

import traceback
print traceback.format_exc()

Jeśli korzystasz z loggingmodułu, możesz wydrukować wyjątek do dziennika (wraz z komunikatem) w następujący sposób:

import logging
log = logging.getLogger()
log.exception("Message for you, sir!")

Jeśli chcesz głębiej kopać i badać stos, przeglądać zmienne itp., Użyj post_mortemfunkcji pdbmodułu wewnątrz bloku oprócz:

import pdb
pdb.post_mortem()

Odkryłem, że ta ostatnia metoda jest nieoceniona podczas tropienia błędów.


1
Myślę, że traceback.print_exc () zrobiłby to samo, co bardziej skomplikowane „” .join-rzecz.
Gurgeh

1
@Gurgeh Tak, ale nie wiem, czy chce go wydrukować, zapisać w pliku, zalogować, czy zrobić z nim coś innego.
Lauritz V. Thaulow

Nie przegłosowałem, ale powiedziałbym, że to dlatego, że powinieneś położyć na sobie ogromne tłuste obawy, mówiąc, że nie powinieneś tego potrzebować, ale oto, jak można to zrobić . A może dlatego, że sugerujesz złapać ogólny wyjątek.
Rik Poggi

10
@Rik Myślę, że możesz bardzo potrzebować tego wszystkiego . Na przykład, jeśli masz program z graficznym interfejsem użytkownika i zapleczem i chcesz przedstawić wszystkie wyjątki od zaplecza jako komunikaty GUI, zamiast zamykania programu za pomocą śledzenia stosu. W takim przypadku powinieneś złapać ogólny wyjątek, utworzyć tekst śledzenia dla okna dialogowego, zarejestrować również wyjątek, a jeśli jest w trybie debugowania, wprowadzić sekcję zwłok.
Lauritz V. Thaulow

18
@RikPoggi: Naiwne myślenie. Istnieje wiele rozsądnych okoliczności, w których trzeba wychwycić wyjątki od kodu innej osoby i nie wiadomo, jakie wyjątki zostaną zgłoszone.
stackoverflowuser2010

63

Uzyskaj nazwę klasy, do której należy obiekt wyjątku:

e.__class__.__name__

a użycie funkcji print_exc () spowoduje także wydrukowanie śladu stosu, który jest niezbędną informacją dla każdego komunikatu o błędzie.

Lubię to:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception, e:
    print 'type is:', e.__class__.__name__
    print_exc()
    # print "exception happened!"

Otrzymasz dane wyjściowe w następujący sposób:

type is: CustomException
Traceback (most recent call last):
  File "exc.py", line 7, in <module>
    raise CustomException("hi")
CustomException: hi

Po wydrukowaniu i analizie kod może zdecydować, aby nie obsługiwać wyjątków i po prostu wykonać raise:

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        raise
    print "handling exception"

Wynik:

special case of CustomException not interfering

I interpreter drukuje wyjątek:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    calculate()
  File "test.py", line 6, in calculate
    raise CustomException("hi")
__main__.CustomException: hi

Po raiseoryginalnym wyjątku nadal propaguje się dalej w górę stosu wywołań. ( Uwaga na ewentualną pułapkę ) Jeśli podniesiesz nowy wyjątek, będzie to oznaczać nowy (krótszy) ślad stosu.

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        #raise CustomException(e.message)
        raise e
    print "handling exception"

Wynik:

special case of CustomException not interfering
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    raise CustomException(e.message)
__main__.CustomException: hi    

Zauważ, że traceback nie obejmuje calculate()funkcji z linii, 9która jest źródłem oryginalnego wyjątku e.


Jeśli chcesz zapisać traceback jako ciąg, można użyć traceback.format_exc()także
Stevoisiak

1
e.__class__.__name__czy to to samo type(e).__name__, co sugeruje powyższa odpowiedź?
information_interchange

1
@information_interchange tak. Pytanie i treść zaakceptowanej odpowiedzi zmieniły się z czasem całkowicie. Szkoda, że ​​inni użytkownicy nie zostali powiadomieni przez maszynę SO :(
Alex

14

Zwykle nie powinieneś wychwytywać wszystkich możliwych wyjątków, try: ... exceptponieważ jest to zbyt szerokie. Po prostu złap te, które mają się wydarzyć z jakiegokolwiek powodu. Jeśli naprawdę musisz, na przykład jeśli chcesz dowiedzieć się więcej o problemach podczas debugowania, powinieneś to zrobić

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.

17
Użycie słowa „nigdy” nigdy nie było tak błędne. Używam try: ... except Exception:wielu rzeczy, np. Korzystania z bibliotek zależnych od sieci lub masażystki danych, która może wysyłać do niej dziwne rzeczy. Oczywiście mam też odpowiednie logowanie. Jest to niezbędne, aby program mógł kontynuować działanie w przypadku pojedynczego błędu w danych wejściowych.
wtorek,

3
Czy próbowałeś kiedyś wychwycić wszystkie wyjątki, które można zgłaszać przy wysyłaniu wiadomości e-mail za pomocą smtplib?
linusg

1
Mogą istnieć pewne szczególne przypadki, w których konieczne jest wychwycenie wszystkich wyjątków, ale na poziomie ogólnym powinieneś po prostu złapać to, czego oczekujesz, aby nie przypadkowo ukryć błędy, których nie spodziewałeś się. Oczywiście dobre logowanie jest również dobrym pomysłem.
hochl

1
Zrozumienie wszystkich wyjątków jest całkowicie uzasadnione. Jeśli dzwonisz do biblioteki innej firmy, możesz nie wiedzieć, jakie wyjątki zostaną zgłoszone w tej bibliotece. W takim przypadku jedynym rozwiązaniem jest wychwycenie wszystkich wyjątków, na przykład zalogowanie ich w pliku.
stackoverflowuser2010

Ok, masz rację, przepiszę moją odpowiedź, aby wyjaśnić, że istnieją ważne przypadki użycia dla wszystkich.
hochl

10

Chyba że somefunction jest to bardzo źle zakodowana starsza funkcja, nie powinieneś potrzebować tego, o co pytasz.

Użyj wielu exceptklauzul do obsługi różnych wyjątków na różne sposoby:

try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

Chodzi przede wszystkim o to, że nie powinieneś wychwytywać ogólnego wyjątku, ale tylko te, które musisz. Jestem pewien, że nie chcesz ukrywać nieoczekiwanych błędów lub błędów.


8
Jeśli korzystasz z biblioteki innej firmy, możesz nie wiedzieć, jakie wyjątki zostaną w niej zawarte. Jak możesz złapać je wszystkie osobno?
stackoverflowuser2010

8

Większość odpowiedzi wskazuje na except (…) as (…):składnię (słusznie), ale jednocześnie nikt nie chce rozmawiać o słoniu w pokoju, w którym słoń jest sys.exc_info()funkcją. Z dokumentacji z sys modułu (podkreślenie moje):

Ta funkcja zwraca krotkę trzech wartości, które dają informacje o aktualnie obsługiwanym wyjątku.
(…)
Jeśli żaden wyjątek nie jest obsługiwany w dowolnym miejscu na stosie, zwracana jest krotka zawierająca trzy wartości None. W przeciwnym razie zwracane są wartości (typ, wartość, traceback). Ich znaczenie to: typ pobiera typ obsługiwanego wyjątku (podklasa BaseException); wartość pobiera instancję wyjątku (instancję typu wyjątku); traceback pobiera obiekt traceback (patrz Podręcznik odniesienia), który hermetyzuje stos wywołań w punkcie, w którym pierwotnie wystąpił wyjątek.

Myślę, że sys.exc_info()można to potraktować jako najbardziej bezpośrednią odpowiedź na pierwotne pytanie: skąd mam wiedzieć, jaki rodzaj wyjątku miał miejsce?


1
To dla mnie poprawna odpowiedź, ponieważ rozwiązuje problem tego, jaki wyjątek występuje, więc co powinienem umieścić zamiast goły except. Ze względu na kompletność exctype, value = sys.exc_info()[:2]poda typ wyjątku, który można następnie zastosować w except.
Ondrej Burkert

5

try: someFunction () oprócz Exception, exc:

#this is how you get the type
excType = exc.__class__.__name__

#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)

#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))

-1 jako użycie exc.__class__.__name__zostało już zasugerowane w odpowiedzi Alexa - stackoverflow.com/a/9824060/95735
Piotr Dobrogost

3

Odpowiedzi te nadają się do debugowania, ale przy programowym testowaniu wyjątku isinstance(e, SomeException)może być przydatne, ponieważ testuje również podklasy SomeException, dzięki czemu można tworzyć funkcje, które będą stosowane do hierarchii wyjątków.


1

Oto jak radzę sobie z wyjątkami. Chodzi o to, aby spróbować rozwiązać problem, jeśli jest to łatwe, a następnie dodać bardziej pożądane rozwiązanie, jeśli to możliwe. Nie rozwiązuj problemu w kodzie, który generuje wyjątek, lub ten kod traci oryginalny algorytm, który należy zapisać od razu. Przekaż jednak dane potrzebne do rozwiązania problemu i zwróć lambda na wypadek, gdybyś nie mógł rozwiązać problemu poza kodem, który go generuje.

path = 'app.p'

def load():
    if os.path.exists(path):
        try:
            with open(path, 'rb') as file:
                data = file.read()
                inst = pickle.load(data)
        except Exception as e:
            inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
    else:
        inst = App()
    inst.loadWidgets()

# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
    class_name = e.__class__.__name__
    print(class_name + ': ' + str(e))
    print('\t during: ' + during)
    return easy

Na razie, ponieważ nie chcę myśleć stycznie do celu mojej aplikacji, nie dodałem żadnych skomplikowanych rozwiązań. Ale w przyszłości, gdy będę wiedział więcej o możliwych rozwiązaniach (ponieważ aplikacja jest bardziej zaprojektowana), mógłbym dodać słownik rozwiązań indeksowanych według during.

W pokazanym przykładzie jednym z rozwiązań może być poszukiwanie danych aplikacji przechowywanych gdzie indziej, np. Jeśli plik „app.p” został przypadkowo usunięty.

Na razie, ponieważ pisanie procedury obsługi wyjątków nie jest mądrym pomysłem (nie znamy jeszcze najlepszych sposobów jej rozwiązania, ponieważ projekt aplikacji będzie ewoluować), po prostu zwracamy łatwą poprawkę, która działa tak, jakbyśmy działali aplikacja po raz pierwszy (w tym przypadku).


0

Aby dodać do odpowiedzi Lauritza, stworzyłem dekorator / opakowanie do obsługi wyjątków i dzienniki opakowań, które typy wyjątków wystąpiły.

class general_function_handler(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type))
    def __call__(self, *args, **kwargs):
        try:
            retval = self.func(*args, **kwargs)
        except Exception, e :
            logging.warning('Exception in %s' % self.func)
            template = "An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

Można to wywołać w metodzie klasy lub samodzielnej funkcji z dekoratorem:

@ moduł obsługi_ogólnej

Zobacz mój blog na pełny przykład: http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/


0

Możesz zacząć zgodnie z zaleceniami Lauritza:

except Exception as ex:

a potem po prostu to print exlubię:

try:
    #your try code here
except Exception as ex:
    print ex

Czy możesz trochę opracować, aby Twoja odpowiedź była samodzielna?
GHC

1
pewnie: możesz wydrukować wychwycony wyjątek w następujący sposób: spróbuj: #wykorzystaj kod tutaj oprócz Wyjątku jako ex: wydrukuj ex teraz błąd zostanie wydrukowany
Gura

-2

Rzeczywisty wyjątek można zarejestrować w następujący sposób:

try:
    i = 1/0
except Exception as e:
    print e

Możesz dowiedzieć się więcej o wyjątkach z Samouczka Python .


-2

Twoje pytanie brzmi: „Jak dokładnie zobaczyć, co się stało w funkcji SomeFunction (), która spowodowała wyjątek?”

Wydaje mi się, że nie pytasz o to, jak poradzić sobie z nieprzewidzianymi wyjątkami w kodzie produkcyjnym (przy założeniu wielu odpowiedzi), ale jak dowiedzieć się, co powoduje konkretny wyjątek podczas programowania.

Najprostszym sposobem jest użycie debugera, który może zatrzymać miejsce, w którym występuje nieprzechwycony wyjątek, najlepiej nie wychodząc, aby można było sprawdzić zmienne. Na przykład PyDev w otwartym środowisku IDE Eclipse może to zrobić. Aby włączyć to w Eclipse, otwórz perspektywę debugowania, wybierz Manage Python Exception Breakpointsz Runmenu i sprawdź Suspend on uncaught exceptions.


-4

Po prostu powstrzymaj się od wyłapywania wyjątku, a informacja zwrotna, którą drukuje Python, powie ci, jaki wyjątek wystąpił.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.