{
 "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
}
