Ето как най-често се справяме с грешките в нашите програми:
"""Модул за зимнината на Митьо Питона"""
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
# Стефан не може да се оправя с това, нека тези отгоре да се грижат
raiseassert <проверка>, [<данни>]assert е да се подсигурите, че важно за вашата програма условие е изпълненоassert test, data е еквивалентно на:if __debug__:
if not test:
raise AssertionError, data
data никак не е задължителноassert рядко се ползва в крайния продукт, а най-вече по време
на разработка за да си спестим главоболия и да сме сигурни в целостта на данните си__debug__ има стойност 1 като може
да бъде променена от вас или от опцията на командния ред -O (оптимизация), която го установява
на Falsedef fib(n):
assert n >= 0
if n <= 1: return n
else: return fib(n-2) + fib(n-1)ExceptionStandardErrorExceptionArithmeticErrorOverflowError, ZeroDivisionError, FloatingPointErrorLookupError IndexError, KeyErrorEnvironmentErrorIOError, OSErrorclass 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