{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "4b201c2f",
   "metadata": {},
   "source": [
    "# Генерация исключений #"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "249ae766",
   "metadata": {},
   "source": [
    "На предыдущей лекции мы начали с вами свое знакомство с исключениями, и в этой лекции мы продолжим разбирать их работу и обсудим более подробно, как обрабатывать исключения, как получать доступ к объекту исключения, а также обсудим отдельные исключения вида `AssertionError` и вопросы производительности исключений."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "5677dc5b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2 No such file or directory\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    with open(\"/file/not/found\") as f:\n",
    "        content = f.read()\n",
    "        \n",
    "except OSError as err:\n",
    "    print(err.errno, err.strerror)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bba1230f",
   "metadata": {},
   "source": [
    "Для того чтобы получить доступ к объекту исключений, нам необходимо воспользоваться конструкцией `except as err`. В данном примере, если будет сгенерировано исключение `OSError`, то сам объект исключений будет связан с переменной `err` и эта переменная `err` будет доступна в блоке `except`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56767c12",
   "metadata": {},
   "source": [
    "У каждого объекта типа исключений есть свои свойства, например, `errno` и `srterror` — это строковое описание ошибки и код ошибки. При помощи этих атрибутов можно получать доступ и обрабатывать исключения нужным вам образом. Очень часто используется атрибут `args` для доступа к атрибутам исключения."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "0c4d7d62",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "файл не существует /file/not/found\n"
     ]
    }
   ],
   "source": [
    "import os.path\n",
    "\n",
    "filename = \"/file/not/found\"\n",
    "\n",
    "try:\n",
    "    if not os.path.exists(filename):\n",
    "        raise ValueError(\"файл не существует\", filename)\n",
    "        \n",
    "except ValueError as err:\n",
    "    message, filename = err.args[0], err.args[1]\n",
    "    print(message, filename)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3e20202",
   "metadata": {},
   "source": [
    "Предположим, мы проверили, что файл не существует, и сгенерировали исключение `ValueError`, передали туда строку и имя файла. Так вот, если мы в блоке `except` обратимся к объекту исключения, то у него будут доступен атрибут `args` — это список из наших параметров и можем делать с ними все, что захотим.\n",
    "\n",
    "Иногда нам может потребоваться стек вызовов при генерации исключения. Стек вызовов можно получить при помощи модуля `traceback` и вызвать метод `print_exc`. Давайте попробуем выполнить этот пример в консоли и посмотрим, что выведется на экран."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "21569499",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Traceback (most recent call last):\n",
      "  File \"<ipython-input-4-adc0be654a54>\", line 4, in <module>\n",
      "    with open(\"/file/not/found\") as f:\n",
      "FileNotFoundError: [Errno 2] No such file or directory: '/file/not/found'\n"
     ]
    }
   ],
   "source": [
    "import traceback\n",
    "\n",
    "try:\n",
    "    with open(\"/file/not/found\") as f:\n",
    "        content = f.read()\n",
    "\n",
    "except OSError as err:\n",
    "    trace = traceback.print_exc()\n",
    "    print(trace)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06d47261",
   "metadata": {},
   "source": [
    "Итак, у нас получилось распечатать стек вызовов. Иногда стек вызовов может вам помочь при разбирательстве причин, при которых возникло исключение. \n",
    "\n",
    "Для того чтобы сгенерировать, собственно, исключение, вам необходима инструкция `raise`. Для генерации исключения мы должны написать `raise` и указать класс исключения. Также можно указывать не только класс, но и объект исключения, и указывать ему дополнительные какие-то свойства. Как я уже говорил, к этим свойствам потом можно будет обратится через объект исключения при помощи атрибута `args`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "b578c4de",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "введите число: w\n",
      "некорректное значение!\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    raw = input(\"введите число: \")\n",
    "    if not raw.isdigit():\n",
    "        raise ValueError\n",
    "        \n",
    "except ValueError:\n",
    "    print(\"некорректное значение!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "28e93b13",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "введите число: w\n",
      "некорректное значение! ('плохое число', 'w')\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    raw = input(\"введите число: \")\n",
    "    if not raw.isdigit():\n",
    "        raise ValueError(\"плохое число\", raw)\n",
    "        \n",
    "except ValueError as err:\n",
    "    print(\"некорректное значение!\", err)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "531ad8cc",
   "metadata": {},
   "source": [
    "В данном случае мы проверяем, что пользователь ввел не число и если так действительно произошло, то генерируем исключение и затем в блоке `except` обрабатываем его.\n",
    "\n",
    "Иногда может случиться так, что вы пишете какую-то функцию и отлавливаете исключения, но вы не знаете, что делать дальше с этим исключением. Например, вы можете просто вывести какую-то информацию на экран и делегировать обработку этого исключения другим функциям — тем, который вызвали вашу. Для этого нужно использовать инструкцию `raise` без параметров."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "0348d576",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "введите число: e\n",
      "некорректное значение! ('плохое число', 'e')\n"
     ]
    },
    {
     "ename": "ValueError",
     "evalue": "('плохое число', 'e')",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-7-03e61a5d927e>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      2\u001b[0m     \u001b[0mraw\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0minput\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"введите число: \"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      3\u001b[0m     \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mraw\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0misdigit\u001b[0m\u001b[1;33m(\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----> 4\u001b[1;33m         \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"плохое число\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mraw\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      5\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      6\u001b[0m \u001b[1;32mexcept\u001b[0m \u001b[0mValueError\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0merr\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mValueError\u001b[0m: ('плохое число', 'e')"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    raw = input(\"введите число: \")\n",
    "    \n",
    "    if not raw.isdigit():\n",
    "        raise ValueError(\"плохое число\", raw)\n",
    "\n",
    "except ValueError as err:\n",
    "    print(\"некорректное значение!\", err)\n",
    "    # делегирование обработки исключения\n",
    "    raise"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75611c95",
   "metadata": {},
   "source": [
    "В данном случае все происходит тоже самое, как в предыдущем примере, генерируются исключения, на экран выводится надпись \"Некорректное значение\", и при помощи инструкции `raise` мы делегируем исключение выше. Если мы попробуем исполнить код, то интерпретатор просто покажет на стандартный вывод информации об этом исключении и прекратит работу нашей программы. Если таких мест, в которых исключения делегируются, очень много, то иногда не понятно по последнему исключению, где именно оно возникло и для этого может использоваться конструкция `raise from Exception`. Давайте посмотрим, как отработает наша программа."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "fea7bab6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "введите число: d\n",
      "ошибка: плохое число d\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "ошибка",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-8-23aebd9f3114>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      4\u001b[0m     \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mraw\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0misdigit\u001b[0m\u001b[1;33m(\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----> 5\u001b[1;33m         \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"плохое число\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mraw\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      6\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mValueError\u001b[0m: ('плохое число', 'd')",
      "\nThe above exception was the direct cause of the following exception:\n",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-8-23aebd9f3114>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      7\u001b[0m \u001b[1;32mexcept\u001b[0m \u001b[0mValueError\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0merr\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      8\u001b[0m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"ошибка:\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0merr\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0merr\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m1\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----> 9\u001b[1;33m     \u001b[1;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"ошибка\"\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mfrom\u001b[0m \u001b[0merr\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: ошибка"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    raw = input(\"введите число: \")\n",
    "    \n",
    "    if not raw.isdigit():\n",
    "        raise ValueError(\"плохое число\", raw)\n",
    "        \n",
    "except ValueError as err:\n",
    "    print(\"ошибка:\", err.args[0], err.args[1])\n",
    "    raise TypeError(\"ошибка\") from err"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00db7b28",
   "metadata": {},
   "source": [
    "Если мы сгенерируем одно исключение, отловим его в блоке `except`, и затем сгенерируем второе исключение `TypeError`, но укажем ему конструкцию `from err`. У нас получилось вызвать исключение и давайте внимательно посмотрим на `stack trace`, который произошел. Мы видим, что произошло исключение `ValueError` — это изначально которое мы сгенерировали. И дальше мы видим его `stack trace`, этого исключения. Дальше мы видим, что после этого исключения произошло второе исключение — `TypeError` и его `stack trace`. Таким образом, если мы будем использовать конструкцию `raise from Exception`, мы сможем отслеживать всю цепочку исключений, которая была сгенерирована."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c41f831b",
   "metadata": {},
   "source": [
    "Говоря об исключениях, нельзя не затронуть инструкцию `assert`. Давайте поговорим, зачем она нужна. По умолчанию, если выполнить инструкцию `assert` с логическим выражением `True`, ничего не произойдет, но если попробовать выполнить инструкцию `assert` с логическим выражением, которое равно `False`, или \"ложь\", то будет сгенерировано исключение `AssertionError`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "64bc3761",
   "metadata": {},
   "outputs": [],
   "source": [
    "assert True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "4c4c07fe",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "ename": "AssertionError",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAssertionError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-10-e99f91a18d62>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32massert\u001b[0m \u001b[1;36m1\u001b[0m \u001b[1;33m==\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mAssertionError\u001b[0m: "
     ]
    }
   ],
   "source": [
    "assert 1 == 0"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "caeb02ab",
   "metadata": {},
   "source": [
    "Также мы можем передать некую дополнительную строку, которая будет потом передана в сам объект `AssertionError`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "381676b1",
   "metadata": {},
   "outputs": [
    {
     "ename": "AssertionError",
     "evalue": "1 не равен 0",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAssertionError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-11-e2b654902c90>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32massert\u001b[0m \u001b[1;36m1\u001b[0m \u001b[1;33m==\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"1 не равен 0\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mAssertionError\u001b[0m: 1 не равен 0"
     ]
    }
   ],
   "source": [
    "assert 1 == 0, \"1 не равен 0\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e799aaab",
   "metadata": {},
   "source": [
    "Давайте разберем кейсы, в каких случаях может использоваться `AssertionError` и инструкция `assert`. \n",
    "\n",
    "Предположим, у нас есть функция, она называется `get_user_by_id`, она нам ищет некоего пользователя по численному идентификатору. Мы должны убедиться в том, что нам действительно передали число, что мы работаем с целым числом. Это можно сделать при помощи `assert` и функции `isinstance`, то есть мы проверяем, является ли входной параметр `id` целым числом. Если это не так, то будет сгенерирована `AssertionError`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "33871e62",
   "metadata": {},
   "outputs": [
    {
     "ename": "AssertionError",
     "evalue": "id должен быть целым числом",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAssertionError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-12-6bcefc7016e1>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0m__name__\u001b[0m \u001b[1;33m==\u001b[0m \u001b[1;34m\"__main__\"\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 6\u001b[1;33m     \u001b[0mget_user_by_id\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"foo\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-12-6bcefc7016e1>\u001b[0m in \u001b[0;36mget_user_by_id\u001b[1;34m(id)\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mget_user_by_id\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mid\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[1;32massert\u001b[0m \u001b[0misinstance\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mid\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mint\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"id должен быть целым числом\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      3\u001b[0m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"выполняем поиск\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0m__name__\u001b[0m \u001b[1;33m==\u001b[0m \u001b[1;34m\"__main__\"\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mAssertionError\u001b[0m: id должен быть целым числом"
     ]
    }
   ],
   "source": [
    "def get_user_by_id(id):\n",
    "    assert isinstance(id, int), \"id должен быть целым числом\"\n",
    "    print(\"выполняем поиск\")\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    get_user_by_id(\"foo\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5196f5e1",
   "metadata": {},
   "source": [
    "Итак, у нас сгенерировалось исключение `AssertionError`. Дело в том, что такие исключения не нужно обрабатывать — эти исключения, они предназначены, скорее, для программистов. То есть при написании наших программ на этапе разработки мы должны видеть, что мы делаем что-то не так, то есть мы передали в функцию некорректное значение, и видим `AssertionError` — наша программа завершила свою работу. В таком случае мы должны как-то реагировать и изменять код нашей программы. Не нужно, например, обрабатывать пользовательский ввод и пытаться обработать исключение `AssetionError` блоком `try except`.\n",
    "\n",
    "Если у нас таких мест будет очень много, то это затронет и производительность нашей программы. Оказывается, можно отключить все инструкции `assert` при помощи флага `−O`. Тогда `AssertionError`, инструкция не будет сгенерирована, потому что `assert` вызван не будет. Этим как раз отличаются эти исключения от обычных пользовательских исключений и исключений стандартной библиотеки. Давайте попробуем сделать это. Запустим с флагом `−O` нашу программу."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "310942f0",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "выполняем поиск\n"
     ]
    }
   ],
   "source": [
    "! python -O ex.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "765e8f49",
   "metadata": {},
   "source": [
    "Итак, мы видим, что наша функция отработала, исключение `AssertionError` не было сгенерировано.\n",
    "\n",
    "Давайте еще поговорим немного о производительности исключений. Несмотря на то, что механизм обработки исключений очень удобный и его очень хорошо использовать в своих программах, тем не менее этот механизм не бесплатен. \n",
    "\n",
    "Рассмотрим пример. У нас есть цикл, и в этом цикле мы обращаемся к несуществующему ключу словаря и каждый раз при этом обращении у нас генерируется исключение `KeyError`. Попробуем при помощи `timeit` замерить, сколько это займет — мы получим некую условную единицу, тысячу операций мы смогли выполнить."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "6f59e6df",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "454 µs ± 9.97 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
     ]
    }
   ],
   "source": [
    "%%timeit\n",
    "my_dict = {\"foo\": 1}\n",
    "\n",
    "for _ in range(1000):\n",
    "    try:\n",
    "        my_dict[\"bar\"]\n",
    "        \n",
    "    except KeyError:\n",
    "        pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "469eb46f",
   "metadata": {},
   "source": [
    "Если мы попробуем реализовать ту же самую задачу, но без использования исключений, например, при помощи конструкции `in`, то есть мы проверяем, что у нас ключик `bar` находится в словаре, и только в этом случае тогда мы обращаемся по ключу к этому словарю. Давайте опять попробуем посмотреть на результаты работы `timeit` для этого примера — и мы видим, что результат отличается на порядок, а иногда он может отличаться и на несколько порядков."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "4c40a7cb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "67.3 µs ± 4.65 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n"
     ]
    }
   ],
   "source": [
    "%%timeit\n",
    "my_dict = {\"foo\": 1}\n",
    "\n",
    "for _ in range(1000):\n",
    "    if \"bar\" in my_dict:\n",
    "        _ = my_dict[\"bar\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85daa3f9",
   "metadata": {},
   "source": [
    "То есть хочу обратить ваше внимание на то, что исключения, особенно если они у вас генерируются в программе очень часто, возможно следует изменить код вашей программы и сделать так, чтобы она работала без исключений, потому, что это очень сильно влияет на производительность вашей итоговой Python программы.\n",
    "\n",
    "Итак, мы обсудили вопросы, которые касаются исключений, поговорили про доступ к объекту исключений, о том как генерируются исключения при помощи `raise`, также обсудили специальные исключения типа `AssertionError`, поговорили о вопросах производительности и нам осталось поговорить о работе с собственными исключениями. В целом работа с собственными исключениями никак не отличается от работы с исключениями стандартной библиотеки и мы попробуем в следующей лекции разобрать работу с собственными исключениями на конкретном примере работы с библиотекой `requests`."
   ]
  }
 ],
 "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
}
