Ето как най-често се справяме с грешките в нашите програми:
"""Модул за зимнината на Митьо Питона""" import jars ERROR = -1 SUCCESS = 0 def prepare_for_winter(): jar = jars.Jar() if jar.clean() == jars.ERROR: print "Couldn't clean Mityo's jar!" return ERROR if jar.fill('python juice') == jars.ERROR: print "Couldn't fill Mityo's jar!" return ERROR if jar.close() == jars.ERROR: print "Couldn't close Mityo's jar!" return ERROR return SUCCESS
Сега да опитаме с изключения:
"""Модул за зимнината на Митьо Питона""" import jars class MityoWinterError: pass def prepare_for_winter(): try: jar = jars.Jar() jar.clean() jar.fill('python juice') jar.close() except jars.Error, error: print "Shit happens"
try: блок except изключения: блок ако се случи някое от описаните изключения … except още изключения: блок ако се случи някое от описаните изключения except: блок ако изключението не е хванато по-горе finally: блок изпълнява се винаги else: блок ако не е възникнала изключителна ситуация
По подразбиране, при неприхванато изключение, Python спира изпълнението на програмата и отпечатва на стандартния изход описание на грешката и реда на извикване на функциите до момента на грешката.
bad.py:
l = [1, 2, 3]
def bad(): print l[3]
bad()
След изпълнение получаваме:
$ python bad.py
Traceback (most recent call last):
File "bad.py", line 3, in <module>
bad()
File "bad.py", line 2, in bad
def bad(): print l[3]
IndexError: list index out of range
Изключенията се използват активно от вградените средства в езика.
def distribute_over(beers):
try:
return 333/beers
except ZeroDivisionError:
return 0
Изключенията са класове, инстанции или низове, като последните не е препоръчително да се ползват.
>>> ZeroDivisionError
<type 'exceptions.ZeroDivisionError'>
Можем да прихванем и по-общ тип изключение (родителски клас):
def distribute_over2(beers):
try:
return 333/beers
except ArithmeticError:
return 0
Ето и доказателство:
>>> issubclass(ZeroDivisionError, ArithmeticError)
True
Тази практика е много логична, тъй като делението на нула е и аритметична грешка и когато прихващаме аритметичните грешки, би трябвало да хванем и делението на нула.
try: x = [] / 4 except TypeError, data: print dataКакво ще има в
data
, зависи от самото изключение, но е прието всички да връщат годна за отпечатване стойност, ако се дадат като аргументи на str
или repr
.
try: doomed() except (NameError, TypeError), data: print data except (MyError, YourError): print "Opps! This shouldn't've hapenned..." except: print "Unknown exception." else: print "It's my happy day!"
except
прихващаме изключения, които не са били хванати до момента. Трябва да бъде поставен след всички други except
-и.file = open('data.txt') try: mymodule.load_info(file) except IOError, data: print "Couldn't read from file: %s" % data except (mymodule.BadDataError, mymodule.InternalError), data: print "Loading failed: %s" % data else: print "Data loaded successfully from file." finally: file.close()
Ако присъства, finally
стои винаги най-отдолу.
LonelyError = "Stefan is lonely!" def make_lonely(): raise LonelyError try: make_lonely() except LonelyError: print LonelyError
Към можем низа да дадем и допълнителна информация:
LonelyError = "Stefan is lonely!" def make_lonely(level): raise MyError, level try: make_lonely(666) except LonelyError, level: print "Stefan's loneliness is at level %d" % level
За да се определи дали едно изключение съвпада с конкретно
except
твърдение двете изключение се сравняват с is
, а не с ==
:
LonelyError0 = "Stefan is lonely!" LonelyError1 = "Stefan is lonely!" def make_lonely(): raise LonelyError0 try: make_lonely() except LonelyError1: print LonelyError1
води до:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 1, in make_lonely
Stefan is lonely!
class StefanError: def __init__(self, msg = "There is something wrong with Stefan!"): self.msg = msg def __repr__(self): return self.msg def __str__(self): return self.__repr__() class LonelyStefanError(StefanError): def __init__(self): StefanError.__init__(self, "Stefan is Lonely!") class StupidStefanError(StefanError): pass def make_lonely(): raise LonelyStefanError() def eat_a_cockroach(): raise StupidStefanError try: make_lonely() except StupidStefanError: print "Stefan has been somehow stupid!" except StefanError, data: print data
2 начина за пораждане:
raise клас, инстанция # инстанцията е от точно този клас
raise инстанция # примерът със самотния Стефан, същото като
raise инстанция.__class__, инстанция
За обратна съвместимост с низовите изключения могат да се използват и следните начини:
raise клас # примерът с тъпия Стефан,
същото като raise клас()
raise клас, arg # същото като raise клас(arg)
raise клас, (arg0, arg1) # същото като raise клас(arg0, arg1)
Пример:
class MyError(): def __init__(self, msg, num): print "%s (%d)" % (msg, num) try: raise MyError, ("Initialized", 99) except MyError: pass
Initialized (99)
class EmotionalStefanError(StefanError): pass class LonelyStefanError(EmotionalStefanError): pass class ConfusedStefanError(EmotionalStefanError): pass class MoneyStefanError(StefanError): pass stefan = Stefan() try: stefan.live_a_day() except MoneyStefanError: stefan.rob_a_bank() # прихващаме по-общия проблем, а не по-частните Lonely и Confused # от инстанцията на проблема психоаналитика може да извлече ценна информация except EmotionalStefanError, problem: stefan.go_to_shrink(problem) except StefanError: stefan.call_911() else: stefan.set_happy_bit() finally: mityo = Mitio() mityo.play_with(Girl(beauty=99, hair='blonde')*3)
EmotionalStefanError = "Stefan has an emotional problem!" LonelyStefanError = "Stefan is lonely!" ConfusedStefanError = "Stefan is confused!" MoneyStefanError = "Stefan is out of money!" stefan = Stefan() try: stefan.live_a_day() except MoneyStefanError: stefan.rob_a_bank() # налага се да опишем всички наследници на EmotionalError на ръка # при всяко добавяне на грешка, трябва да я добавяме и тук except (EmotionalStefanError, LonelyStefanError, ConfusedStefanError), problem: stefan.go_to_shrink(problem) # тук пък трябва да опишем всички други грешки... # което си е ад за поддръжка, почти като москвич осмак except StefanError: stefan.call_911() else: stefan.set_happy_bit() finally: stefan.play_with(Girl(beauty=99, hair='blonde')*3)
raise
:
try: stefan.live_a_day() except GirlfriendStefanError: stefan.lonely = True # Стефан не може да се оправя с това, нека тези отгоре да се грижат raise
assert <проверка>, [<данни>]
assert
е да се подсигурите, че важно за вашата програма условие е изпълненоassert test, data
е еквивалентно на:if __debug__: if not test: raise AssertionError, data
data
никак не е задължителноassert
рядко се ползва в крайния продукт, а най-вече по време
на разработка за да си спестим главоболия и да сме сигурни в целостта на данните си__debug__
има стойност 1
като може
да бъде променена от вас или от опцията на командния ред -O
(оптимизация), която го установява
на False
def fib(n): assert n >= 0 if n <= 1: return n else: return fib(n-2) + fib(n-1)
Exception
StandardError
Exception
ArithmeticError
OverflowError
, ZeroDivisionError
, FloatingPointError
LookupError
IndexError
, KeyError
EnvironmentError
IOError
, OSError
class Exception
Какво може да направи Exception
за нас?
>>> class MyError(Exception): pass
>>> raise MyError("Failed, you have!", -1)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
__main__.MyError: ('Failed, you have!', -1)
>>> error = MyError("Failed, you have!", -1)
>>> error.args
('Failed, you have!', -1)
args
__str__
, така че да връща нещо като: map(str, self.args)
try: for box in boxes: for jar in box.jars(): jar.has(throw=True, colour='velvet', eyes='cheese') except JarFound, good_jar: print "We found the jar! Its name is %s" % good_jar.name else: print "I couldn't find it :("
def func(): try: … # някъде тук възниква IndexError except: … # тук хващаме всичко и отпечатваме съобщение за грешка try: func() except IndexError: # нъцки, няма да го хванем тук …
except
главно в най-високото ниво на програмата сиtry: stefan.live_a_day() except (StefanWantsACracker, StefanWantsADrink, StefanWantsAnIsland, StefanWantsA333YearOldWhiskey, StefanNeedsPanties, StefanNeedsNewSlippers), thing: you.buy(whom=stefan, what=thing)
except (StefanWants, StefanNeeds), thing:
…
finally