9. Изключения и with

9. Изключения и with

9. Изключения и with

23 април 2014

Традицията повелява

import jars

ERROR = -1
SUCCESS = 0

def the_winter_is_coming():
    jar = jars.Jar()
    if jar.clean() == jars.ERROR:
        print("Something went terribly wrong!")
        return ERROR
    if jar.fill('python juice') == jars.ERROR:
        print("Something went terribly wrong!")
        return ERROR
    if jar.close() == jars.ERROR:
        print("Something went terribly wrong!")
        return ERROR
    return SUCCESS

Традициите не са...

import jars

def the_winter_is_coming():
    try:
        jar = jars.Jar()
        jar.clean()
        jar.fill('python juice')
        jar.close()
    except jars.Error:
        print("Something went terribly wrong!")

Синтаксис и семантика

try
    # блок
except Изключение
    # блок за хващане и обработка на някое от описаните изключения


except ДругоИзключение
    # блок за хващане и обработка на някое от описаните изключения
except
    # блок за хващане и обработка на което и да е изключение(== except BaseException)
else
    # блок изпълняващ се, ако не е възникнала изключителна ситуация
finally
    # блок изпълнява се винаги

подробно тук

Стандартни изключения

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- ArithmeticError
         +-- ...
      +-- AssertionError
      +-- AttributeError
      +-- NameError
         +-- UnboundLocalError
      +-- OSError
         +-- ...
      +-- RuntimeError
         +-- NotImplementedError
      +-- SyntaxError
         +-- IndentationError
              +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
         +-- ...
      +-- Warning
           +-- ...

Източника

Повече информация рядко е излишна

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 have hapenned...")
except:
    print("Unknown exception.")
else:
    print("It's my happy day!")

С празен except прихващаме изключения, които не са били хванати до момента. Трябва да бъде поставен след всички други except-и.

finally

file = open('data.txt')
try:
    e.load_info(file)
except IOError as data:
    print("Couldn't read from file:", data)
except (e.BadDataError, e.InternalError) as data:
    print('Loading failed:', data)
else:
    print('Data loaded successfully from file.')
finally:
    file.close()

Ако присъства, finally стои винаги най-отдолу.

Създаване на изключения

class WinterError(Exception):
    def __init__(self):
        self.issuer, self.message = 'You are', 'NOT PREPARED!!11!едно!1'

class WildlingError(WinterError):
    def __init__(self):
        super().__init__()
        self.message = 'We are going to light the biggest fire the North has ever seen!1!!'

class YgetteError(WildlingError):
    def __init__(self):
        super().__init__()
        self.message = 'You know nothing, John Snow!'

class WhiteWalker(WinterError):
    def __init__(self):
        super().__init__()
        self.message = 'Rawr!'

def winter_is_coming(): raise WinterError

def winter_is_here(): raise WhiteWalker()

референция

Ескалиране на грешката

try:
    bender.live_a_day()
except BenderError:
    bender.boned = True
    # Бендър не може да се оправя с това, нека тези отгоре да се грижат
    raise

Подходи

Нека обобщим

Няколко неща, за които може да ползваме изключения:

обработка на грeшки:

Finally!

безусловно извършване на заключителни действия — finally

Искаме да обърнем реда на редовете на файл?

try:
    source_file = open(src, 'r')
    buffer = []
    try:
        buffer = source_file.readlines()
    finally:
        source_file.close()

    target_file = open(target, 'w')
    try:
        for line in reversed(buffer):
            target_file.write(line)
    finally:
        target_file.close()
except IOError:
    print("Tough luck, junior")

Too long; didn't read?

buffer = []
try:
    with open(src) as source_file:
        buffer = source_file.readlines()
    with open(target) as target_file:
        for line in reversed(buffer):
            target_file.write(line)
except IOError:
    print("Much better, now, ain't it?")

with гарантира, че файлът ще бъде затворен автоматично.

with

with израз [as име]:
   блок

with нагледно

with open('/etc/passwd') as source_file:
    buffer = source_file.readlines()
print('Done!')

е същото като

source_file = open('/etc/passwd').__enter__()
try:
  buffer = source_file.readlines()
  source_file.__exit__(None, None, None)
except Exception:
  source_file.__exit__(*sys.exc_info())
print('Done!')

Малък пример

class Manager:
    def __enter__(self):
        print("I've been entered!")
        return 42
    def __exit__(self, type, value, traceback):
        print("I've been exited!")

with Manager() as something:
    print("Am I inside?")
    print(something)

# I've been entered!
# Am I inside?
# 42
# I've been exited!

with с няколко аргумента

with foo() as f, bar() as b:
   ...

е същото като

with foo() as f:
    with bar() as b:
       ...

contextlib

Вграденият модул contextlib ни предлага три много полезни Context Manager-а:

contextlib

closing

contextlib.closing вика метода close на обекта, с който работим, след изпълнение на блока:

class closing(object):
    def __init__(self, thing): self.thing = thing
    def __enter__(self): return thing
    def __exit__(self, type, value, traceback):
        self.thing.close()

...и ви позволява да пишете следното:

from contextlib import closing
from urllib.request import urlopen

with closing(urlopen('http://www.python.org')) as page:
    for line in page:
        print(line)

Въпроси?