Ето как най-често се справяме с грешките в нашите програми:
"""Модул за зимнината на Митьо Питона"""
import jars
ERROR = -1
SUCCESS = 0
def prepare_for_winter():
jar = jars.Jar()
if jar.clean() == jars.ERROR:
print("Shit happens")
return ERROR
if jar.fill('python juice') == jars.ERROR:
print("Shit happens")
return ERROR
if jar.close() == jars.ERROR:
print("Shit happens")
return ERROR
return SUCCESS
Сега да опитаме с изключения:
"""Модул за зимнината на Митьо Питона"""
import jars
class MityoWinterError(Exception): pass
def prepare_for_winter():
try:
jar = jars.Jar()
jar.clean()
jar.fill('python juice')
jar.close()
except jars.Error:
print("Shit happens")
try:
блок
except изключения:
блок ако се случи някое от описаните изключения
…
except още изключения:
блок ако се случи някое от описаните изключения
except:
блок ако изключението не е хванато по-горе
else:
блок ако не е възникнала изключителна ситуация
finally:
блок изпълнява се винаги
По подразбиране, при неприхванато изключение, Python спира изпълнението на програмата и отпечатва на стандартния изход описание на грешката и реда на извикване на функциите до момента на грешката.
bad.py:
l = [1, 2, 3]
def bad(): print(l[3])
bad()
След изпълнение получаваме:
[~]$ python3.0 bad.py
Traceback (most recent call last):
File "bad.py", line 3, in
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
Изключенията са инстанции на подкласове на BaseException.
>>> ZeroDivisionError
<class 'ZeroDivisionError'>
Можем да прихванем и по-общ тип изключение (родителски клас):
def distribute_over2(beers):
try:
return 333/beers
except ArithmeticError:
return 0
Ето и доказателство:
>>> issubclass(ZeroDivisionError, ArithmeticError)
True
Тази практика е много логична, тъй като делението на нула е и аритметична грешка и когато прихващаме аритметичните грешки, би трябвало да хванем и делението на нула.
try: x = [] / 4
except TypeError as data: print(data)
Какво ще има в data, зависи от самото изключение, но е прието всички да връщат годна за отпечатване стойност, ако се дадат като аргументи на str или repr.
try:
doomed()
except (NameError, TypeError) as 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 as data:
print("Couldn't read from file:", data)
except (mymodule.BadDataError, mymodule.InternalError) as data:
print('Loading failed:', data)
else:
print('Data loaded successfully from file.')
finally:
file.close()
Ако присъства, finally стои винаги най-отдолу.
class XmasError(Exception):
def __init__(self):
self.issuer, self.message = 'Robosanta', 'watches you'
class NaughtyError(XmasError):
def __init__(self):
super().__init__()
self.message = 'You were very naughty this year!'
class AreYouDeadYetError(XmasError):
def __init__(self):
super().__init__()
self.message = 'Are you dead yet?'
def confess_sins(): raise NaughtyError
def celebrate_xmas(): raise AreYouDeadYetError
try:
celebrate_xmas()
except AreYouDeadYetError as e:
print(e.issuer, '--', e.message)
except XmasError:
print('Climbing to the sky. Never wonder why. Tailgunner...')
2 начина за пораждане:
raise клас # създава се инстация като __init__ се вика само със selfraise инстанция
Пример:
class MyError(Exception):
def __init__(self, who, why):
self.who, self.why = who, why
def __str__(self):
return '{0} did it because {1}'.format(self.who, self.why)
>>> try:
raise MyError('I', 'I want to rule the world!')
except MyError as e:
print(e)
I did it because I want to rule the world!
>>>
class EmotionalError(BenderError): pass
class FryDeadError(EmotionalError): pass
class NotFamousError(EmotionalError): pass
class MoneyError(BenderError): pass
bender = Bender()
try:
bender.live_a_day()
except MoneyError:
bender.rob_a_friend()
# прихващаме по-общия проблем, а не по-частните FryDead и NotFamous
# от инстанцията на проблема психоаналитика може да извлече ценна информация
except EmotionalError as problem:
bender.drink_and_run_away_from(problem)
except BenderError:
bender.activate_self_destruct_sequence()
else:
bender.watch_tv()
finally:
bender.build_own_ship_with_blackjack_and_hookers()
raise:
try:
bender.live_a_day()
except BenderError:
bender.boned = True
# Бендър не може да се оправя с това, нека тези отгоре да се грижат
raiseЗа всеки метод определяме:
def factorial(n):
...
x = factorial(7)
assert <проверка>, [<данни>]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)BaseException, но най-съществените наследяват от ExceptionStandardErrorExceptionArithmeticErrorOverflowError, ZeroDivisionError, FloatingPointErrorLookupError IndexError, KeyErrorEnvironmentErrorIOError, OSErrorclass ExceptionКакво може да направи Exception за нас?
class MyError(Exception): pass
>>> raise MyError('Bite', 'my', 'shiny', 'metal', 'ass', '!')
Traceback (most recent call last):
File "", line 1, in
__main__.MyError: ('Bite', 'my', 'shiny', 'metal', 'ass', '!')
>>> error = MyError('Make booze for HIM', 'Make it twice')
>>> error.args
('Make booze for HIM', 'Make it twice')
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 as good_jar:
print("We found the jar! Its name is {0}".format(good_jar.name))
else:
print("I couldn't find it :(")
def func():
try:
… # някъде тук възниква IndexError
except:
… # тук хващаме всичко и отпечатваме съобщение за грешка
try:
func()
except IndexError: # нъцки, няма да го хванем тук
…except главно в най-високото ниво на програмата сиtry:
bender.live_a_day()
except (BenderWantsACracker, BenderWantsADrink,
BenderWantsAnIsland, BenderWantsA333YearOldWhiskey,
BenderNeedsOil, BenderNeedsAHooker) as thing:
you.buy(whom=bender, what=thing) except (BenderWants, BenderNeeds) as thing:
…finally