Ето как най-често се справяме грешките в нашите програми:
"""Модул за зимнината на Митьо Питона"""
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:
raise MityoWinterError
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 badfunc
def badfunc(): 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 = "Mityo is lonely!"
def make_lonely(): raise LonelyError
try:
make_lonely()
except LonelyError:
print LonelyError
Можем към низа да дадем и допълнителна информация:
LonelyError = "Mityo is lonely!"
def make_lonely(level): raise MyError, level
try:
make_lonely(666)
except LonelyError, level:
print "Mityo's loneliness is at level %d" % level
За да се определи дали едно изключение съвпада с конкретно
except
твърдение двете изключение се сравняват с is
, а не с ==
:
LonelyError0 = "Mityo is lonely!"
LonelyError1 = "Mityo 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
Mityo is lonely!
class MityoError:
def __init__(self, msg = "There is something wrong with Mityo!"):
self.msg = msg
def __repr__(self): return self.msg
def __str__(self): return self.__repr__()
class LonelyMityoError(MityoError):
def __init__(self):
MityoError.__init__(self, "Mityo is Lonely!")
class StupidMityoError(MityoError): pass
def make_lonely(): raise LonelyMityoError()
def eat_a_cockroach(): raise StupidMityoError
try:
make_lonely()
except StupidMityoError:
print "Mityo has been somehow stupid!"
except MityoError, 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 EmotionalMityoError(MityoError): pass
class LonelyMityoError(EmotionalMityoError): pass
class ConfusedMityoError(EmotionalMityoError): pass
class MoneyMityoError(MityoError): pass
mityo = Mityo()
try:
mityo.live_a_day()
except MoneyMityoError:
mityo.rob_a_bank()
# прихващаме по-общия проблем, а не по-частните Lonely и Confused
# от инстанцията на проблема психоаналитика може да извлече ценна информация
except EmotionalMityoError, problem:
mityo.go_to_shrink(problem)
except MityoError:
mityo.call_911()
else:
mityo.set_happy_bit()
finally:
mityo.play_with(Girl(beauty=99, hair='blonde')*3)
EmotionalMityoError = "Mityo has an emotional problem!"
LonelyMityoError = "Mityo is lonely!"
ConfusedMityoError = "Mityo is confused!"
MoneyMityoError = "Mityo is out of money!"
mityo = Mityo()
try:
mityo.live_a_day()
except MoneyMityoError:
mityo.rob_a_bank()
# налага се да опишем всички наследници на EmotionalError на ръка
# при всяко добавяне на грешка, трябва да я добавяме и тук
except (EmotionalMityoError, LonelyMityoError, ConfusedMityoError), problem:
mityo.go_to_shrink(problem)
# тук пък трябва да опишем всички други грешки...
# което си е ад за поддръжка, почти като москвич осмак
except MityoError:
mityo.call_911()
else:
mityo.set_happy_bit()
finally:
mityo.play_with(Girl(beauty=99, hair='blonde')*3)
raise
: try:
mityo.live_a_day()
except GirlfriendMityoError:
mityo.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
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:
mityo.live_a_day()
except (MityoWantsACracker, MityoWantsADrink,
MityoWantsAnIsland, MityoWantsA333YearOldWhiskey,
MityoNeedsPanties, MityoNeedsNewSlippers), thing:
you.buy(whom=mityo, what=thing)
except (MityoWants, MityoNeeds), thing:
…
finally