Odpowiedzi:
Jak ręcznie zgłosić / zgłosić wyjątek w Pythonie?
Użyj najbardziej szczegółowego konstruktora wyjątków, który semantycznie pasuje do Twojego problemu .
Bądź konkretny w swojej wiadomości, np .:
raise ValueError('A very specific bad thing happened.')
Unikaj generowania Exception
. Aby go złapać, musisz złapać wszystkie inne, bardziej szczegółowe wyjątki, które go dzielą.
raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.
Na przykład:
def demo_bad_catch():
try:
raise ValueError('Represents a hidden bug, do not catch this')
raise Exception('This is the exception you expect to handle')
except Exception as error:
print('Caught this error: ' + repr(error))
>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)
A bardziej szczegółowe połowy nie złapią ogólnego wyjątku:
def demo_no_catch():
try:
raise Exception('general exceptions not caught by specific handling')
except ValueError as e:
print('we will not catch exception: Exception')
>>> demo_no_catch()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling
raise
oświadczenieraise ValueError('A very specific bad thing happened')
co również pozwala na przekazanie dowolnej liczby argumentów do konstruktora:
raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz')
Dostęp do tych argumentów ma args
atrybut Exception
obiektu. Na przykład:
try:
some_code_that_may_raise_our_value_error()
except ValueError as err:
print(err.args)
odciski
('message', 'foo', 'bar', 'baz')
W Pythonie 2.5 dodano rzeczywisty message
atrybut na BaseException
rzecz zachęcania użytkowników do podklas wyjątków i zaprzestania ich używania args
, ale wprowadzenie message
i oryginalne wycofanie argumentów zostało wycofane .
except
klauzulaWewnątrz klauzuli wyjątku możesz na przykład zarejestrować, że wystąpił określony typ błędu, a następnie ponownie go podnieść. Najlepszym sposobem na to przy zachowaniu śladu stosu jest użycie instrukcji bare raise. Na przykład:
logger = logging.getLogger(__name__)
try:
do_something_in_app_that_breaks_easily()
except AppError as error:
logger.error(error)
raise # just this!
# raise AppError # Don't do this, you'll lose the stack trace!
Możesz zachować stacktrace (i wartość błędu) sys.exc_info()
, ale jest to o wiele bardziej podatne na błędy i ma problemy z kompatybilnością między Pythonem 2 i 3 , wolisz używać samego systemu raise
do przebijania.
Aby wyjaśnić - sys.exc_info()
zwraca typ, wartość i traceback.
type, value, traceback = sys.exc_info()
To jest składnia w Pythonie 2 - zauważ, że to nie jest kompatybilne z Pythonem 3:
raise AppError, error, sys.exc_info()[2] # avoid this.
# Equivalently, as error *is* the second object:
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]
Jeśli chcesz, możesz zmodyfikować to, co dzieje się z Twoim nowym przebiciem - np. Ustawiając nowe args
dla instancji:
def error():
raise ValueError('oops!')
def catch_error_modify_message():
try:
error()
except ValueError:
error_type, error_instance, traceback = sys.exc_info()
error_instance.args = (error_instance.args[0] + ' <modification>',)
raise error_type, error_instance, traceback
I zachowaliśmy cały ślad podczas modyfikowania argumentów. Zauważ, że nie jest to najlepsza praktyka i jest to niepoprawna składnia w Pythonie 3 (utrudniająca zachowanie kompatybilności).
>>> catch_error_modify_message()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in catch_error_modify_message
File "<stdin>", line 2, in error
ValueError: oops! <modification>
W Pythonie 3 :
raise error.with_traceback(sys.exc_info()[2])
Ponownie: unikaj ręcznej manipulacji śladami. Jest mniej wydajny i bardziej podatny na błędy. A jeśli używasz wątków i sys.exc_info
możesz nawet uzyskać niewłaściwe śledzenie (szczególnie jeśli używasz obsługi wyjątków dla przepływu sterowania - czego osobiście chciałbym uniknąć).
W Pythonie 3 można łączyć wyjątki, które zachowują identyfikatory:
raise RuntimeError('specific message') from error
Być świadomym:
Można je łatwo ukryć, a nawet dostać się do kodu produkcyjnego. Chcesz zgłosić wyjątek, a ich zrobienie spowoduje wyjątek, ale nie ten, który był przeznaczony!
Prawidłowy w Pythonie 2, ale nie w Pythonie 3, jest następujący:
raise ValueError, 'message' # Don't do this, it's deprecated!
Działa tylko w dużo starszych wersjach Pythona (2.4 i niższych), możesz nadal widzieć ludzi generujących ciągi znaków:
raise 'message' # really really wrong. don't do this.
We wszystkich nowoczesnych wersjach spowoduje to podniesienie a TypeError
, ponieważ nie podbijasz BaseException
typu. Jeśli nie sprawdzasz właściwego wyjątku i nie masz recenzenta, który jest świadomy problemu, może on zostać wprowadzony do produkcji.
Zgłaszam wyjątki, aby ostrzec konsumentów o moim interfejsie API, jeśli używają go niepoprawnie:
def api_func(foo):
'''foo should be either 'baz' or 'bar'. returns something very useful.'''
if foo not in _ALLOWED_ARGS:
raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))
„Chcę celowo popełnić błąd, aby przejść do wyjątku”
Możesz utworzyć własne typy błędów, jeśli chcesz wskazać, że coś jest nie tak z aplikacją, po prostu podklasuj odpowiedni punkt w hierarchii wyjątków:
class MyAppLookupError(LookupError):
'''raise this when there's a lookup error for my app'''
i użycie:
if important_key not in resource_dict and not ok_to_be_missing:
raise MyAppLookupError('resource is missing, and that is not ok.')
raise sys.exc_info()[0], (sys.exc_info()[1], my_extra_info), sys.exc_info()[2]
wydaje się robić to, co chcę i nigdy nie miałem z tym problemów. Ale wydaje się to zuchwałe i nie jest przyjętą praktyką. Czy jest lepszy sposób?
Exception
twojej klasy nadrzędnej - możesz podklasować coś bardziej szczegółowego i powinieneś to zrobić, jeśli ma to sens.
AppError
wyjątku. Może być lepiej użyć wbudowanego błędu, takiego jakAttributeError
NIE Rób tego . Podnoszenie gołe
Exception
jest absolutnie nie słuszne zrobić; zamiast tego zobacz doskonałą odpowiedź Aarona Halla .
Nie można uzyskać dużo bardziej pythonicznego niż to:
raise Exception("I know python!")
Zobacz dokumentację instrukcji wywoływania Pythona, jeśli chcesz uzyskać więcej informacji.
W Python3 istnieją 4 różne składnie wyjątków rasowania:
1. raise exception
2. raise exception (args)
3. raise
4. raise exception (args) from original_exception
1. zgłaszaj wyjątek vs. 2. zgłaszaj wyjątek (argumenty)
Jeśli użyjesz raise exception (args)
do zgłoszenia wyjątku, args
zostanie on wydrukowany podczas drukowania obiektu wyjątku - jak pokazano w poniższym przykładzie.
#raise exception (args)
try:
raise ValueError("I have raised an Exception")
except ValueError as exp:
print ("Error", exp) # Output -> Error I have raised an Exception
#raise execption
try:
raise ValueError
except ValueError as exp:
print ("Error", exp) # Output -> Error
3. podniesienie
raise
instrukcja bez żadnych argumentów ponownie podnosi ostatni wyjątek. Jest to przydatne, jeśli po wykonaniu wyjątku trzeba wykonać pewne czynności, a następnie ponownie go podnieść. Ale jeśli wcześniej nie było wyjątku, raise
oświadczenie podnosi TypeError
wyjątek.
def somefunction():
print("some cleaning")
a=10
b=0
result=None
try:
result=a/b
print(result)
except Exception: #Output ->
somefunction() #some cleaning
raise #Traceback (most recent call last):
#File "python", line 8, in <module>
#ZeroDivisionError: division by zero
4. zgłaszać wyjątek (argumenty) od oryginalnego_wyjątku
Ta instrukcja służy do tworzenia łańcucha wyjątków, w którym wyjątek zgłoszony w odpowiedzi na inny wyjątek może zawierać szczegóły oryginalnego wyjątku - jak pokazano w poniższym przykładzie.
class MyCustomException(Exception):
pass
a=10
b=0
reuslt=None
try:
try:
result=a/b
except ZeroDivisionError as exp:
print("ZeroDivisionError -- ",exp)
raise MyCustomException("Zero Division ") from exp
except MyCustomException as exp:
print("MyException",exp)
print(exp.__cause__)
Wynik:
ZeroDivisionError -- division by zero
MyException Zero Division
division by zero
exception(args)
niżexception (args)
raise exception(args) from None
powiedzieć, że obecnie aktywny wyjątek został obsłużony i nie jest już interesujący. W przeciwnym razie, jeśli zgłosisz wyjątek w except
bloku i nie zostanie on obsłużony, identyfikatory śledzenia dla obu wyjątków zostaną pokazane oddzielone komunikatem „Podczas obsługi powyższego wyjątku wystąpił inny wyjątek”
W typowym przypadku, w którym musisz rzucić wyjątek w odpowiedzi na niektóre nieoczekiwane warunki i którego nigdy nie zamierzasz złapać, ale po prostu zawieść szybko, aby umożliwić debugowanie stamtąd, jeśli kiedykolwiek się zdarzy - najbardziej logiczny wydaje się być AssertionError
:
if 0 < distance <= RADIUS:
#Do something.
elif RADIUS < distance:
#Do something.
else:
raise AssertionError("Unexpected value of 'distance'!", distance)
ValueError
niż AssertionError
dlatego, że nie ma problemu z twierdzeniem (ponieważ nie ma tu miejsca) - problem dotyczy wartości. Jeśli naprawdę chcesz AssertionError
w tym przypadku, napisz assert distance > 0, 'Distance must be positive'
. Ale nie powinieneś błędu sprawdzać w ten sposób, ponieważ asercje można wyłączyć ( python -O
).
-O
.
Najpierw przeczytaj istniejące odpowiedzi, to tylko dodatek.
Zauważ, że możesz zgłaszać wyjątki z argumentami lub bez.
Przykład:
raise SystemExit
wychodzi z programu, ale możesz chcieć wiedzieć, co się stało, więc możesz tego użyć.
raise SystemExit("program exited")
spowoduje to wydrukowanie „programu zakończonego” na stderr przed zamknięciem programu.
raise SystemExit()
byłby to lepszy wybór? Dlaczego pierwszy w ogóle działa?
Innym sposobem na zgłoszenie wyjątku jest assert
. Możesz użyć aser, aby sprawdzić, czy warunek jest spełniony, jeśli nie, to wzrośnie AssertionError
. Aby uzyskać więcej informacji, zobacz tutaj .
def avg(marks):
assert len(marks) != 0,"List is empty."
return sum(marks)/len(marks)
mark2 = [55,88,78,90,79]
print("Average of mark2:",avg(mark2))
mark1 = []
print("Average of mark1:",avg(mark1))
Uwaga: zdarzają się chwile, kiedy chcesz obsłużyć ogólne wyjątki. Jeśli przetwarzasz kilka plików i rejestrujesz swoje błędy, możesz chcieć wychwycić każdy błąd, który wystąpi dla pliku, zaloguj go i kontynuuj przetwarzanie pozostałych plików. W takim przypadku a
try:
foo()
except Exception as e:
print(str(e)) # Print out handled error
zablokuj dobry sposób, aby to zrobić. Nadal będziesz chciał raise
stosować wyjątki, abyś wiedział, co one oznaczają.
W tym celu powinieneś nauczyć się instrukcji podnoszenia Pythona. Powinien być przechowywany w bloku try. Przykład -
try:
raise TypeError #remove TypeError by any other error if you want
except TypeError:
print('TypeError raised')
raise
to, czego potrzebowałem, aby móc wykonać niestandardowe debugowanie błędów na wielu poziomach wykonania kodu bez przerywania śledzenia stosu.