You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

742 lines
34 KiB
Plaintext

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

{
"cells": [
{
"cell_type": "markdown",
"id": "adeac7e8",
"metadata": {},
"source": [
"# Классы исключений и их обработка #"
]
},
{
"cell_type": "markdown",
"id": "e85bd50b",
"metadata": {},
"source": [
"В предыдущих лекциях мы несколько раз упоминали про исключения в Python. Сегодня мы как раз обсудим то, как устроены исключения в Python. Мы поговорим про генерацию исключений, что при этом происходит, обсудим типы исключений, а также рассмотрим, как обрабатывать исключения в Python.\n",
"\n",
"Для начала давайте попробуем вызвать исключения и посмотрим, что при этом произойдет. Для этого нам потребуется консоль. Давайте попробуем написать простенькую программу на Python. Пусть это будет файл `zero.py`. Попробуем вывести строчку `1/0`. Давайте запустим нашу программу."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "3a0fab6c",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Traceback (most recent call last):\n",
" File \"zero.py\", line 1, in <module>\n",
" 1/0\n",
"ZeroDivisionError: division by zero\n"
]
}
],
"source": [
"! python zero.py"
]
},
{
"cell_type": "markdown",
"id": "546ec448",
"metadata": {},
"source": [
"Итак, мы видим, что произошло. При делении на 0 возникло исключение, и при возникновении исключения на стандартный вывод печатается информация о его типе, дополнительная информация о исключении, а также стек вызовов.\n",
"\n",
"Давайте посмотрим, что у нас произошло. Мы видим, на экран напечаталось - тип исключения — `ZeroDivisionError`, дополнительная информация и сам стек. Стек у нас пока небольшой, и для того чтобы посмотреть на стек настоящей программы, давайте посмотрим дополнительный пример."
]
},
{
"cell_type": "markdown",
"id": "4e1a1ee8",
"metadata": {},
"source": [
"Пусть у нас есть программа, которая считает количество строк в исходном файле, который подали на вход. Выглядит она нам не очень интересно как, то есть есть некая функция `main`, в функции `main` вызывается другая функция. Эта функция вызывает функцию `wc` с переданным файлом и печатает на экран некую строчку с именем файла и количеством строк в нем. А функция `wc` открывает этот файл и итератором проходится по всем строкам в этом файле.\n",
"\n",
"```python\n",
"import sys\n",
"\n",
"def wc(filename):\n",
" count = 0\n",
" \n",
" with open(filename) as f:\n",
" for _ in f:\n",
" count += 1\n",
" \n",
" return count\n",
"\n",
"def process_file(filename):\n",
" count = wc(filename)\n",
" \n",
" print(f\"file: {filename} has {count} lines\")\n",
"\n",
"def _main():\n",
" process_file(sys.argv[1])\n",
" \n",
"if __name__ == \"__main__\":\n",
" _main()\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "8f7658a8",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Traceback (most recent call last):\n",
" File \"wc.py\", line 21, in <module>\n",
" _main()\n",
" File \"wc.py\", line 18, in _main\n",
" process_file(sys.argv[1])\n",
" File \"wc.py\", line 13, in process_file\n",
" count = wc(filename)\n",
" File \"wc.py\", line 6, in wc\n",
" with open(filename) as f:\n",
"FileNotFoundError: [Errno 2] No such file or directory: '/etc/test'\n"
]
}
],
"source": [
"! python wc.py /etc/test"
]
},
{
"cell_type": "markdown",
"id": "7e6043d2",
"metadata": {},
"source": [
"Давайте попробуем вызвать данную программу, передадим ей либо несуществующий файл, либо файл, недоступный для чтения, и посмотрим, что при этом произойдет. Итак, у нас есть наша программа. Давайте попробуем ей передать файл, которого нет. Это файл `/etc/test`. Итак, как видим, сгенерировалось исключение.\n",
"\n",
"При этом Python остановил свою работу, и на экране мы видим тип исключения — это `FileNotFoundError`, а также дополнительную информацию - код ошибки, текстовые сообщения, что файла `/etc/test` нет. Также теперь мы видим стек вызовов, теперь он у нас побольше. Давайте распечатаем текст самой программы и немножко разберемся, что же значит этот стек вызовов.\n",
"\n",
"Давайте посмотрим, что на строке 6 фукнции `wc` была вызвана функция `open`. Давайте еще раз сгенерируем наше исключение. Итак, мы сгенерировали исключение, и посмотрим на текст нашей программы. Итак, мы видим, что в строке 6 нашей программы была вызвана функция `open`. Именно это привело к генерации исключения. Давайте дальше пройдемся по стеку вызовов и раскрутим его наверх. Мы видим, что наша функция `wc` была вызвана на строке 13, вот видим ее. Это было сделано функцией `process_file`. Если мы будем раскручивать стек дальше, то мы сможем отследить всю последовательность вызовов функции, которая привела к исключению. Таким образом, это нам очень может сильно помочь, если мы будем разбираться с чужими исключениями, которые вдруг внезапно возникли в программе и не были обработаны программистом.\n",
"\n",
"Какие типы исключений бывают? В Python бывают по большому счету два типа исключений.\n",
"\n",
"Первый — это исключения из стандартной библиотеки в Python. Они генерируются, собственно, самой библиотекой. То есть когда мы вызываем функцию стандартной библиотеки, например, мы видели, как функция `open` сгенерировала исключение `PermissionError`.\n",
"\n",
"А также второй тип исключений — это пользовательские исключения. Они могут быть сгенерированы и обработаны самим программистом при написании программ на Python.\n",
"\n",
"Давайте посмотрим на иерархию исключений в стандартной библиотеке Python. Все исключения наследуются от базового класса `BaseException`. Существуют несколько системных исключений, например, `SystemExit` — это исключение генерируется, если мы вызвали функцию `OSExit`. `KeyboardInterrupt` — это исключение генерируется, если мы нажали сочетание клавиш `Ctrl + C` и так далее."
]
},
{
"cell_type": "markdown",
"id": "0ffb082b",
"metadata": {},
"source": [
"```\n",
"BaseException\n",
"+-- SystemExit\n",
"+-- KeyboardInterrupt\n",
"+-- GeneratorExit\n",
"+-- Exception\n",
" +-- StopIteration\n",
" +-- AssertionError\n",
" +-- AttributeError\n",
" +-- LookupError\n",
" +-- IndexError\n",
" +-- KeyError\n",
" +-- OSError\n",
" +-- SystemError\n",
" +-- TypeError\n",
" +-- ValueError\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "1dd418b8",
"metadata": {},
"source": [
"Все остальные исключения генерируется от базового класса `Exception`. Именно от этого класса нужно будет порождать и свои исключения, чем мы и займемся дальше.\n",
"\n",
"Давайте посмотрим и обсудим некоторые исключения из базы библиотеки, такие как, например, `AttributeError`, `IndexError`, `KeyError`, `TypeError`, и попробуем их вызвать. Для этого нам снова потребуется консоль. Давайте запустим интерпретатор. Объявим простенький класс. Пусть это будет класс, который ничего не делает."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "b09aeca0",
"metadata": {},
"outputs": [],
"source": [
"class MyClass:\n",
" pass"
]
},
{
"cell_type": "markdown",
"id": "9f2183bb",
"metadata": {},
"source": [
"Давайте создадим объект этого класса и попробуем обратиться к атрибуту `foo`."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "6ac92428",
"metadata": {},
"outputs": [
{
"ename": "AttributeError",
"evalue": "'MyClass' object has no attribute 'foo'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-11-0c1a83a28f93>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0mobj\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mMyClass\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mobj\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfoo\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mAttributeError\u001b[0m: 'MyClass' object has no attribute 'foo'"
]
}
],
"source": [
"obj = MyClass()\n",
"obj.foo"
]
},
{
"cell_type": "markdown",
"id": "e2657a83",
"metadata": {},
"source": [
"Как мы видим, сгенерировалось исключение `AttributeError`.\n",
"\n",
"Давайте попробуем объявить словарик. Пусть в нем будет один элемент. "
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "e57ffe19",
"metadata": {},
"outputs": [],
"source": [
"d = {\"foo\": 1}"
]
},
{
"cell_type": "markdown",
"id": "7535f072",
"metadata": {},
"source": [
"Попробуем обратиться к несуществующему ключику."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "69060ba8",
"metadata": {},
"outputs": [
{
"ename": "KeyError",
"evalue": "'bar'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mKeyError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-13-586c1efcf0c1>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0md\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m\"bar\"\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mKeyError\u001b[0m: 'bar'"
]
}
],
"source": [
"d[\"bar\"]"
]
},
{
"cell_type": "markdown",
"id": "87cb9673",
"metadata": {},
"source": [
"Итак, у нас получилось сгенерировать исключение `KeyError`.\n",
"\n",
"Если бы мы объявили список из двух элементов и обратились, например, к 10-му элементу, у нас бы сгенерировался `IndexError`."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "e134d942",
"metadata": {},
"outputs": [
{
"ename": "IndexError",
"evalue": "list index out of range",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mIndexError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-14-8c556715db69>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0ml\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m2\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0ml\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mIndexError\u001b[0m: list index out of range"
]
}
],
"source": [
"l = [1, 2]\n",
"l[10]"
]
},
{
"cell_type": "markdown",
"id": "aeb59421",
"metadata": {},
"source": [
"Также если мы, например, попробовали преобразовать к целому числу строчку, мы бы получили `ValueError`."
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "cd6a04fc",
"metadata": {},
"outputs": [
{
"ename": "ValueError",
"evalue": "invalid literal for int() with base 10: 'fdf'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-15-0921b230eda9>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"fdf\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mValueError\u001b[0m: invalid literal for int() with base 10: 'fdf'"
]
}
],
"source": [
"int(\"fdf\")"
]
},
{
"cell_type": "markdown",
"id": "51be3abc",
"metadata": {},
"source": [
"Или если бы попробовали сложить число и строку, получили бы `TypeError`."
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "f6ccfd3b",
"metadata": {},
"outputs": [
{
"ename": "TypeError",
"evalue": "unsupported operand type(s) for +: 'int' and 'str'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-16-b780703cc5f9>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;36m1\u001b[0m \u001b[1;33m+\u001b[0m \u001b[1;34m\"1\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mTypeError\u001b[0m: unsupported operand type(s) for +: 'int' and 'str'"
]
}
],
"source": [
"1 + \"1\""
]
},
{
"cell_type": "markdown",
"id": "6ea521e0",
"metadata": {},
"source": [
"Все эти исключения — из стандартной библиотеки. Вам при работе очень часто придется сталкиваться с ними, и теперь вы знаете, как они выглядят, и при каких обстоятельствах они могут быть сгенерированы.\n",
"\n",
"Если исключение сгенерировано, то, как я уже говорил, Python-интерпретатор остановит свою работу и на экран будет выведен стек вызовов и информация о типе исключений.\n",
"\n",
"И нам это не всегда хочется, чтобы такое поведение было по умолчанию, и поэтому нужно как-то обработать сгенерированные исключения. Обработать его можно при помощи блока `try except`. То есть у нас есть код, который потенциально может генерировать исключения, мы этот код обрамляем в блок `try except`, и тем самым при генерации исключений управление будет передано в блок `except`."
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "a88352fe",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Error\n"
]
}
],
"source": [
"try:\n",
" 1 / 0\n",
"\n",
"except:\n",
" print(\"Error\")"
]
},
{
"cell_type": "markdown",
"id": "ed95ab0c",
"metadata": {},
"source": [
"Таким образом можно отловить все исключения, которые генерируются в блоке `try except`.\n",
"\n",
"Если мы в блоке `except` укажем исключение, например в данном случае `Exception`, то мы будем отлавливать исключения всех типов, у которых класс `Exception` является родителем."
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "c48a05fb",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Error\n"
]
}
],
"source": [
"try:\n",
" 1 / 0\n",
"\n",
"except Exception:\n",
" print(\"Error\")"
]
},
{
"cell_type": "markdown",
"id": "4427af89",
"metadata": {},
"source": [
"В целом неправильно ждать любые исключения, и это может привести к непредвиденным сюрпризам работы вашей программы.\n",
"\n",
"Давайте рассмотрим следующий пример. Мы в бесконечном цикле просим пользователя ввести число, преобразовываем его строку к числу целому."
]
},
{
"cell_type": "markdown",
"id": "b8d34e47",
"metadata": {},
"source": [
"```python\n",
"while True:\n",
" try:\n",
" raw = input(\"Input number: \")\n",
" number = int(raw)\n",
" break\n",
" \n",
" except:\n",
" print(\"not number\")\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "0f185a81",
"metadata": {},
"source": [
"В данном случае может возникнуть исключение `ValueError`. Однако, мы в своей программе отлавливаем все исключения. Давайте попробуем ее исполнить и посмотрим, как она работает. Так, нас просят ввести число. Давайте введем. Нас снова просят ввести число. Давайте, например, попросим наш интерпретатор завершить свою работу и нажмем `Ctrl + C`.\n",
"\n",
"Как мы видим, у нас это не получилось сделать, то есть наша программа стала вести себя непредвиденным образом. Она нас снова опять заставляет ввести число. Это как раз говорит о том, что нужно обрабатывать конкретные исключения, и в данном случае правильным вариантом данной программы была бы обработка исключений `ValueError`."
]
},
{
"cell_type": "markdown",
"id": "eef3c8bd",
"metadata": {},
"source": [
"```python\n",
"while True:\n",
" try:\n",
" raw = input(\"Input number: \")\n",
" number = int(raw)\n",
" break\n",
" \n",
" except ValueError:\n",
" print(\"not number\")\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "d7444dfb",
"metadata": {},
"source": [
"Если мы попробуем снова запустить данную программу и нажмем `Ctrl + C`. Давайте введем сначала неправильное число, нажмем `Ctrl + C`, то возникнет исключение и его никто не обработает, и наша программа завершит свою работу. Поэтому не следует обрабатывать все исключения и оставлять пустым блок `except`. Имейте это в виду.\n",
"\n",
"Также у блока `try except` может быть блок `else`. Блок `else` вызывается в том случае, если никакого исключения не произошло."
]
},
{
"cell_type": "markdown",
"id": "28cb2cbe",
"metadata": {},
"source": [
"```python\n",
"while True:\n",
" try:\n",
" raw = input(\"Input number: \")\n",
" number = int(raw)\n",
" break\n",
" \n",
" except ValueError:\n",
" print(\"not number\")\n",
" \n",
" else:\n",
" break\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "545ef10a",
"metadata": {},
"source": [
"Если нам нужно обработать несколько исключений, мы можем использовать несколько блоков `except` и указать разные классы для обработки исключения. Причем в каждом блоке `except` может быть свой собственный обработчик."
]
},
{
"cell_type": "markdown",
"id": "2a06fab7",
"metadata": {},
"source": [
"```python\n",
"while True:\n",
" try:\n",
" raw = input(\"Input number: \")\n",
" number = int(raw)\n",
" break\n",
" \n",
" except ValueError:\n",
" print(\"not number\")\n",
" \n",
" except KeyboardInterrupt:\n",
" print(\"exit\")\n",
" break\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "dd9d3b79",
"metadata": {},
"source": [
"Например, если мы в данном примере ввели некорректное число, то мы еще раз продолжим работу нашего цикла. В противном случае, если мы нажали `Ctrl + C`, то мы прекратим цикл. Именно таким образом можно обработать несколько исключений."
]
},
{
"cell_type": "markdown",
"id": "2db929d8",
"metadata": {},
"source": [
"Если обработчик исключений выглядит одинаково, то несколько исключений можно передать в виде списка в блок `except` и также обработать сгенерированные исключения."
]
},
{
"cell_type": "markdown",
"id": "8185d03b",
"metadata": {},
"source": [
"```python\n",
"while True:\n",
" try:\n",
" raw = input(\"Input number: \")\n",
" number = int(raw)\n",
" total_count /= number\n",
" break\n",
" \n",
" except (ValueError, ZeroDivisionError):\n",
" print(\"not number\")\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "076e56ef",
"metadata": {},
"source": [
"В данном случае мы ожидаем два исключения, например, что пользователь ввел некорректное число, либо если он ввел 0, то данная программа сгенерирует `ZeroDivisionError`, и мы его отловим.\n",
"\n",
"Мы уже с вами обсуждали классы и наследования в классах, и вот у `exception`'ов есть своя иерархия, и сделана она неспроста. Также при помощи родительского класса можно обрабатывать несколько исключений. Давайте, например, посмотрим на иерархию классов `LookupError`."
]
},
{
"cell_type": "markdown",
"id": "8ce76d09",
"metadata": {},
"source": [
"```\n",
"+-- LookupError\n",
" +-- IndexError\n",
" +-- KeyError\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "047d3573",
"metadata": {},
"source": [
"Этот класс является родительским для исключений `IndexError` и `KeyError`. Мы можем это проверить при помощи известных нам функций `issubclass`. Рассмотрим следующий пример."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "42250fda",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"issubclass(KeyError, LookupError)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "b891412f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"issubclass(IndexError, LookupError)"
]
},
{
"cell_type": "markdown",
"id": "a7b4d748",
"metadata": {},
"source": [
"У нас есть некая структура данных, это наша база данных, которая хранит по цветам надписи на футболках. Нам необходимо получать доступ к этим надписям по цветам.\n",
"\n",
"Пользователя просим ввести цвет, просим ввести номер по порядку, а затем обращаемся к нашей структуре данных по ключу, а затем по индексу. И если пользователь введет, например, некорректный цвет, то будет сгенерировано исключение `KeyError`, а если пользователь введет недопустимый индекс, то будет вызвано исключение `IndexError`. Все эти исключения мы можем обработать при помощи базового класса `LookupError`. Иногда это очень удобно и требуется для как раз обработки пользовательских исключений. "
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "be5fda81",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"введите цвет: red\n",
"введите номер по порядку: 1\n",
"вы выбрали: flower\n"
]
}
],
"source": [
"database = {\n",
" \"red\": [\"fox\", \"flower\"],\n",
" \"green\": [\"peace\", \"M\", \"python\"]\n",
"}\n",
"\n",
"try:\n",
" color = input(\"введите цвет: \")\n",
" number = input(\"введите номер по порядку: \")\n",
" label = database[color][int(number)]\n",
" print(\"вы выбрали:\", label)\n",
"\n",
"except LookupError:\n",
" # except (IndexError, KeyError):\n",
" print(\"Объект не найден\")"
]
},
{
"cell_type": "markdown",
"id": "a0060f21",
"metadata": {},
"source": [
"Также у исключений есть дополнительный блок `finally`. Рассмотрим проблему. Например, мы открываем файл, читаем строки, обрабатываем как-то эти строки, и в процессе работы нашей программы возникает исключение, которое мы не ждем, например. В таком случае файл закрыт не будет. У нас эти открытые файловые дескрипторы могут накапливаться, что, в принципе, не хорошо. Таким же образом могут накапливаться открытые сокеты, или не освобождаться память, всё что угодно.\n",
"\n",
"Для контроля таких ситуаций существуют, во-первых, контекстные менеджеры, а во-вторых, можно использовать блок `finally` в исключениях. Выглядит это таким образом, как представлено на слайде."
]
},
{
"cell_type": "markdown",
"id": "0a0ce1a0",
"metadata": {},
"source": [
"```python\n",
"f = open(\"/etc/hosts\")\n",
"\n",
"try:\n",
" for line in f:\n",
" print(line.rstrip(\"\\n\"))\n",
" 1 / 0\n",
" \n",
"except OSError:\n",
" print(\"ошибка\")\n",
" \n",
"finally:\n",
" f.close()\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "991a0c2a",
"metadata": {},
"source": [
"Мы пишем блок `finally` и вызываем метод `close` для нашего объекта `f`. В данном случае блок `finally` будет выполнен в любом случае. Возникло исключение или не возникло — блок `finally` будет выполнен.\n",
"\n",
"Итак, мы поговорили с вами про исключения, посмотрели на то, как они выглядят, как выглядит `stack trace`, обсудили, как ведет себя Python при генерации исключений. Также посмотрели на то, какие типы исключений бывают - это пользовательские исключения и исключения стандартной библиотеки. Также рассмотрели иерархию исключений в стандартной библиотеке и поговорили про их обработку. Мы продолжим обсуждать то, как устроены исключения, на следующей лекции."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}