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

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

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

21 март 2016

Но първо…

Имахте домашно.

Крайния срок беше преди два часа.

58 от вас са предали решения.

От общо 80 записани.

Но първо…

Миналата година тези цифри бяха 38/176.

imagine

И в тоя ред на мисли

Имаме някои забележки

Имена

хубави имена за класове:

Имена

нехубави имена за класове:

Имена

Още (по-)нехубави имена за класове:

Прости правила

Имената на класовете ви трябва да са винаги UpperCamelCase.

Прости правила

Имената на методите ви трябва да са винаги snake_case.

Прости правила

Неща кръстни със SCREAMING_SNAKE_CASE имена се предполага да са константи.

Едно очевидно

Повтарящия се код трябва да бъде скрит зад някаква абстракция(клас, функция, whatever)

Нива на влагане

Ние се стараем да ви даваме задачи, които могат да се решават с елегантен и прост код.

Код > Думи

def evaluate(self, **variables):
  rhs = 0
  lhs = 0
  result = 0
  for element in self.expression_structure:
      if isinstance(element, Operator):
          operator = element
      if isinstance(element, Variable):
          if element.name in self.variable_names:
              for key, value in variables.items():
                  if key == element.name:
                      element.value = value
          if lhs == 0:
              lhs = element.value
          else:
              rhs = element.value
      if isinstance(element, Constant):
          if lhs == 0:
              lhs = element.value
          else:
              rhs = element.value
      if lhs != 0 and rhs != 0 and isinstance(operator, Operator):
          result = operator.function(lhs, rhs)
  return result

The Zen of Python

Няколко малки точки от него:

type checking

почти винаги е грешния начин за това, което искате да направите разчитайки на duck typing

And now for someth–

Поискай разрешение

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 Изключение/tuple от Изключения:
    # блок за хващане и обработка на някое от описаните изключения


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 WhiteWalkerError(WinterError):
    pass

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")

TL;DR

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!") # iYKwIM
        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 self.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)

Въпроси?