Ето как най-често се справяме с грешките в нашите програми:
"""Модул за зимнината на Митьо Питона""" 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__ се вика само със self
raise инстанция
Пример:
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
(оптимизация), която го установява
на False
def fib(n): assert n >= 0 if n <= 1: return n else: return fib(n-2) + fib(n-1)
BaseException
, но най-съществените наследяват от Exception
StandardError
Exception
ArithmeticError
OverflowError
, ZeroDivisionError
, FloatingPointError
LookupError
IndexError
, KeyError
EnvironmentError
IOError
, OSError
class 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