1.02M

exceptions

1.

Исключения
Докладчик: Евграфов Михаил

2.

Исключения
2

3.

Примеры исключений
# пример синтаксической ошибки
num = 5
if num % 2 == 1 print("number is odd")
...
SyntaxError: invalid syntax
# пример логической ошибки
division = num / 0
...
ZeroDivisionError: division by zero
3

4.

Иерархия исключений
KeyboardInterrupt
SystemExit
BaseException
Exception
GeneratorExit
BaseExceptionGroup
4

5.

Иерархия исключений
KeyboardInterrupt
SystemExit
BaseException
Exception
GeneratorExit
BaseExceptionGroup
5

6.

try-except
while True:
try:
...
...
except:
...
...
try-блок
except-блок /
обработчик ошибок
6

7.

Отсутствие исключения
# input -> 5
try:
input_value = input("Enter a number: ")
number = int(input_value)
result = 10 / number
print(f"{result = }")
except ValueError:
print(f"input is invalid: {input_value}")
7

8.

Отсутствие исключения
# input -> 5
try:
input_value = input("Enter a number: ")
number = int(input_value) # OK
result = 10 / number # OK
print(f"{result = }")
except ValueError:
print(f"input is invalid: {input_value}")
8

9.

Отсутствие исключения
# input -> 5
try:
input_value = input("Enter a number: ")
number = int(input_value) # OK
result = 10 / number # OK
print(f"{result = }")
except ValueError:
print(f"input is invalid: {input_value}")
# result = 2.0
9

10.

Обрабатываемое исключение
# input -> "string"
try:
input_value = input("Enter a number: ")
number = int(input_value) # ValueError
result = 10 / number
print(f"{result = }")
except ValueError:
print(f"input is invalid: {input_value}")
10

11.

Обрабатываемое исключение
# input -> "string"
try:
input_value = input("Enter a number: ")
number = int(input_value) # ValueError
result = 10 / number
print(f"{result = }")
except ValueError: # совпадение
print(f"input is invalid: {input_value}")
# input is invalid: string
11

12.

Обрабатываемое исключение
# input -> "string"
try:
input_value = input("Enter a number: ")
number = int(input_value) # ValueError
result = 10 / number
print(f"{result = }")
except ValueError: # совпадение
print(f"input is invalid: {input_value}")
# input is invalid: string
12

13.

Непойманное исключение
# input -> 0
try:
input_value = input("Enter a number: ")
number = int(input_value) # OK
result = 10 / number
print(f"{result = }")
except ValueError:
print(f"input is invalid: {input_value}")
13

14.

Непойманное исключение
# input -> 0
try:
input_value = input("Enter a number: ")
number = int(input_value) # OK
result = 10 / number # ZeroDivisionError
print(f"{result = }")
except ValueError: # несовпадение
print(f"input is invalid: {input_value}")
...
ZeroDivisionError: division by zero
14

15.

Общий обработчик
# input -> "five"
try:
input_value = input("Enter a number: ")
number = int(input_value) # ValueError
result = 10 / number
print(f"{result = }")
except (ValueError, ZeroDivisionError):
print(f"input is invalid: {input_value}")
# input is invalid: five
15

16.

Общий обработчик
# input -> 0
try:
input_value = input("Enter a number: ")
number = int(input_value)
result = 10 / number # ZeroDivisionError
print(f"{result = }")
except (ValueError, ZeroDivisionError):
print(f"input is invalid: {input_value}")
# input is invalid: 0
16

17.

Несколько обработчиков
# input -> "five"
try:
input_value = input("Enter a number: ")
number = int(input_value) # ValueError
result = 10 / number
print(f"{result = }")
except ValueError:
print(f"input is invalid: {input_value}")
except ZeroDivisionError:
print("0 as input is forbidden")
# input is invalid: five
17

18.

Несколько обработчиков
# input -> 0
try:
input_value = input("Enter a number: ")
number = int(input_value)
result = 10 / number # ZeroDivisionError
print(f"{result = }")
except ValueError:
print(f"input is invalid: {input_value}")
except ZeroDivisionError:
print("0 as input is forbidden")
# 0 as input is forbidden
18

19.

Порядок выбора обработчика
# input -> "five"
try:
input_value = input("Enter a number: ")
number = int(input_value) # ValueError
result = 10 / number
print(f"{result = }")
except ValueError: # обрабатывает только ValueError
print(f"input is invalid: {input_value}")
except Exception as exc: # обрабатывает "все"
print(f"general handler got {type(exc).__name__}")
# input is invalid: five
19

20.

Порядок выбора обработчика
# input -> 0
try:
input_value = input("Enter a number: ")
number = int(input_value) # OK
result = 10 / number # ZeroDivisionError
print(f"{result = }")
except ValueError: # обрабатывает только ValueError
print(f"input is invalid: {input_value}")
except Exception as exc: # обрабатывает "все"
print(f"general handler got {type(exc).__name__}")
# general handler got ZeroDivisionError
20

21.

Неверный порядок обработчиков
# input -> "five"
try:
input_value = input("Enter a number: ")
number = int(input_value) # ValueError
result = 10 / number
print(f"{result = }")
except Exception as exc:
print(f"general handler got {type(exc).__name__}")
except ValueError:
print(f"input is invalid: {input_value}")
# general handler got ValueError
21

22.

try-except-else
# input -> "five"
try:
input_value = input("Enter a number: ")
number = int(input_value)
except ValueError:
print(f"input is invalid: {input_value}")
else:
print(f"{number = }")
22

23.

try-except-else
# input -> "five"
try:
input_value = input("Enter a number: ")
number = int(input_value) # ValueError
except ValueError:
print(f"input is invalid: {input_value}")
else:
print(f"{number = }")
23

24.

try-except-else
# input -> "five"
try:
input_value = input("Enter a number: ")
number = int(input_value) # ValueError
except ValueError:
print(f"input is invalid: {input_value}")
else:
print(f"{number = }")
# input is invalid: five
24

25.

try-except-else
# input -> 42
try:
input_value = input("Enter a number: ")
number = int(input_value) # OK
except ValueError:
print(f"input is invalid: {input_value}")
else:
print(f"{number = }")
25

26.

try-except-else
# input -> 42
try:
input_value = input("Enter a number: ")
number = int(input_value) # OK
except ValueError:
print(f"input is invalid: {input_value}")
else:
print(f"{number = }")
# number = 42
26

27.

try-except-else
# input -> 42
try:
input_value = input("Enter a number: ")
number = int(input_value) # OK
except ValueError:
print(f"input is invalid: {input_value}")
else:
print(f"{number = }")
# number = 42
27

28.

raise
# явное создание исключения
raise ValueError("invalid value")
...
ValueError: invalid value
# неявное создание исключения
raise ValueError
...
ValueError:
28

29.

re-raise
try:
raise ValueError("invalid value")
except ValueError as exc:
print(f"exception info: {exc}")
raise
# exception info: invalid value
...
ValueError: invalid value
29

30.

Цепочка исключений
def connect_to_db() -> None:
raise ConnectionError("fail to connect")
try:
connect_to_db()
except ConnectionError:
raise RuntimeError("transaction failed")
...
ConnectionError: fail to connect
During handling of the above exception, another exception occurred:
...
RuntimeError: transaction failed
30

31.

raise from
def connect_to_db() -> None:
raise ConnectionError("fail to connect")
try:
connect_to_db()
except ConnectionError as exc:
raise RuntimeError("transaction failed") from exc
...
ConnectionError: fail to connect
The above exception was the direct cause of the following exception:
...
RuntimeError: transaction failed
31

32.

raise from None
def connect_to_db() -> None:
raise ConnectionError("fail to connect")
try:
connect_to_db()
except ConnectionError:
raise RuntimeError("transaction failed") from None
...
RuntimeError: transaction failed
32

33.

Механизм распространения ошибок
def raise_() -> None:
print("before raise")
raise Exception("exc from raise_")
print("after raise")
def call_raise() -> None:
print("before calling raise_")
raise_()
print("after calling raise_")
33

34.

Механизм распространения ошибок
def process_exception() -> None:
try:
print("before exception raising")
call_raise()
print("after exception raising")
except Exception as exc:
print(f"process exception:
{exc}")
34

35.

Механизм распространения ошибок
process_exception()
# before exception raising
# before calling raise_
# before raise
raise Exception(...)
Стек вызовов
raise_
call_raise
process_exception
35

36.

Механизм распространения ошибок
process_exception()
# before exception raising
# before calling raise_
# before raise
raise Exception(...)
Стек вызовов
call_raise
process_exception
36

37.

Механизм распространения ошибок
Стек вызовов
process_exception()
# before exception raising
# before calling raise_
# before raise
raise Exception(...)
# process exception: exc from raise_
process_exception
37

38.

LBYL vs EAFP
registry = {}
key = "key"
# LBYL
if key in registry:
print(registry[key])
# EAFP
try:
print(registry[key])
except KeyError:
pass
сначала
думаем,
потом делаем
сначала
делаем,
потом думаем
38

39.

Эксперимент
def get_key_lbyl(
dict_: dict[str, str], key: str
) -> str | None:
if key in dict_:
return dict_[key]
return None
def get_key_eafp(
dict_: dict[str, str], key: str
) -> str | None:
try:
return dict_[key]
except KeyError:
return None
39

40.

Эксперимент: редкие ошибки
import time
dict_ = {"key": "value"}
time_start = time.time()
for _ in range(10_000_000):
get_key_lbyl(dict_, "key")
print(f"time spent: {time.time() - time_start:.2f}")
# time spent: 0.98
40

41.

Эксперимент: редкие ошибки
import time
dict_ = {"key": "value"}
time_start = time.time()
for _ in range(10_000_000):
get_key_eafp(dict_, "key")
print(f"time spent: {time.time() - time_start:.2f}")
# time spent: 0.87
41

42.

Эксперимент: частые ошибки
import time
dict_ = {}
time_start = time.time()
for _ in range(10_000_000):
get_key_lbyl(dict_, "key")
print(f"time spent: {time.time() - time_start:.2f}")
# time spent: 0.87
42

43.

Эксперимент: частые ошибки
import time
dict_ = {}
time_start = time.time()
for _ in range(10_000_000):
get_key_eafp(dict_, "key")
print(f"time spent: {time.time() - time_start:.2f}")
# time spent: 2.91
43

44.

Контекстные менеджеры
44

45.

Захват и освобождение ресурсов
# захват ресурса
file = open("file.txt", mode="w")
file.write("Hello, World!")
# освобождение ресурса
file.close()
45

46.

Проблема
# захват ресурса
file = open("file.txt", mode="w")
file.write("Hello, World!") # FAIL
# освобождение ресурса не произойдет
file.close()
46

47.

try-finally
try:
file = open("file.txt", mode="w")
file.write("Hello, World!") # OK
print("succesfully write to file")
finally:
file.close()
print("successfully close file")
# succesfully write to file
# successfully close file
47

48.

try-finally
try:
file = open("file.txt", mode="w")
file.write("Hello, World!") # OK
print("succesfully write to file")
finally:
file.close()
print("successfully close file")
# successfully close file
...
UnsupportedOperation: not writable
48

49.

try-except-finally
try:
file = open("file.txt", mode="r")
file.write("Hello, World!")
print("succesfully write to file")
except Exception as exc:
print(exc)
finally:
file.close()
print("successfully close file")
# not writable
# successfully close file
49

50.

try-except-else-finally
try:
# выполняется всегда
pass
except Exception:
# выполняется при ошибке в try-блоке
pass
else:
# выполняется, если в try-блоке нет ошибок
pass
finally:
# выполняется всегда
pass
50

51.

with
# файл гарантированно будет закрыт
# что бы ни произошло
with open("test.txt", "w") as file:
file.write("Hello, World!")
51

52.

Множественный with
with (
open("source.txt", "r") as file_read,
open("sink.txt", "w") as file_write,
):
readen_data = file_read.read()
file_write.write(readen_data)
52

53.

Семинар
53
English     Русский Правила