{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "08735fdb",
   "metadata": {},
   "source": [
    "# Байткод #"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "764f3e24",
   "metadata": {},
   "source": [
    "А сейчас давайте поговорим про байт-код.\n",
    "\n",
    "Помните, когда мы рассматривали организацию кода на Python, мы импортировали функции из модулей и пакетов, и Python автоматически создавал файлики с расширением.pyc. Я сказал, что это файлики, в которых содержится байт-код, то есть скомпилированное представление вашего кода в форме, удобной Python для интерпретирования.\n",
    "\n",
    "Давайте сейчас подробнее посмотрим, что это значит. Для этого мы создадим модуль, назовем его `mymodule`.\n",
    "\n",
    "Мы с вами это уже умеем делать, и внутри определим функцию `multiply`, точно такую же, которую мы с вами уже писали. Она принимает два аргумента и возвращает их произведение.\n",
    "\n",
    "```python\n",
    "def multiply(a, b):\n",
    "    return a * b\n",
    "```\n",
    "\n",
    "Функция определена, теперь мы можем ее импортировать. Давайте воспользуемся конструкцией `from`. `from mymodule import multiply`. `multiply` — это функция."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "03f059a0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "importing mypackage\n"
     ]
    }
   ],
   "source": [
    "from mymodule import multiply"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c3e8648",
   "metadata": {},
   "source": [
    "Как я уже сказал, практически всё в Python — это объект, не исключение и функция. Если мы посмотрим с помощью функции `dir` методы, которые содержит функция, мы увидим, что их много, в том числе у функции есть атрибут `__code__` с двумя подчеркиваниями, это `code object`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "1374e3bc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']\n"
     ]
    }
   ],
   "source": [
    "print(dir(multiply))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "43989f9d",
   "metadata": {},
   "source": [
    "По сути, это объект, который оборачивает тот код, который вы пишете. У этого объекта есть метод `co_code`, который содержит байт-код. Это байт-код, который Python сгенерировал для этой функции."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "a3373ab6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'|\\x00|\\x01\\x14\\x00S\\x00'\n"
     ]
    }
   ],
   "source": [
    "print(multiply.__code__.co_code)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb706b6d",
   "metadata": {},
   "source": [
    "Что это значит? Это значит, что наша функция была преобразована в байт-код, который представляет собой набор инструкций. В данном случае это байтовая строка, которая содержит инструкцию одну за другой, которые подаются на вход интерпретатору Python.\n",
    "\n",
    "Интерпретатор Python, по большому счету, это большая конструкция `switch` кода на C, которая, в зависимости от входящей инструкции байт-кода, предпринимает то или иное действие."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "33872151",
   "metadata": {},
   "source": [
    "Как мы видим, байт-код нечеловекочитаемый Мы можем привести его в человекочитаемый вид, используя модуль `dis` стандартной библиотеки Python.\n",
    "\n",
    "Давайте это сделаем. Заимпортируем модуль `dis` и с помощью метода `dis` модуля `dis` дисассемблируем нашу функцию в человекочитаемый вид. Что мы видим?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "e2ccab2f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "          0 LOAD_FAST                0 (0)\n",
      "          2 LOAD_FAST                1 (1)\n",
      "          4 BINARY_MULTIPLY\n",
      "          6 RETURN_VALUE\n"
     ]
    }
   ],
   "source": [
    "from dis import dis\n",
    "\n",
    "dis(multiply.__code__.co_code)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42c24b47",
   "metadata": {},
   "source": [
    "Мы видим, что наша функция на самом деле преобразовывается в байт-код, который состоит из четырех инструкций. Каждая из этих инструкций делает какое-то действие. Давайте посмотрим на каждую инструкцию подробно.\n",
    "\n",
    "Первая — это `LOAD_FAST`, это инструкция, которая загружает содержимое переменной `a` в память и помещает его на стек. Стек — это структура данных, последним пришёл - первым вышел (LIFO), которая определена на уровне C и используется Python для того, чтобы манипулировать ходом выполнения программы.\n",
    "\n",
    "Как только переменная `a` помещена на стек, мы переходим ко второй инструкции, которая также `LOAD_FAST`, она делает то же самое со значением переменной `b`, помещает значение переменной `b` на стек. Далее.\n",
    "\n",
    "Следующая инструкция — это `BINARY_MULTIPLY`. `BINARY_MULTIPLY` говорит интерпретатору Python, что нужно взять два самых верхних значения на стеке и произвести их умножение. Заметьте, что мы могли передать в функцию значение любого типа. Могли передать целые числа, могли вещественные. Могли бы передать даже строки, например. Но в результате преобразования нашей функции в байт-код байт-код получился тем же самым. Типизация начинает по большому счету работать тогда, когда Python выполняет ту или иную инструкцию.\n",
    "\n",
    "В данном случае, когда выполняется инструкция `BINARY_MULTIPLY`, Python посмотрит на тип объектов, и, например, если это два числа, произведет умножение двух чисел. Результат умножения он поместит на вершину стека.\n",
    "\n",
    "И последняя инструкция — это `RETURN_VALUE`. `RETURN_VALUE` берет вершину стека, это результат умножения наших значений `a` и `b`, и возвращает его. По большому счету, это то, как работает Python, как устроен байт-код, и любой код на Python, который вы будете писать, будет превращаться, компилироваться в байт-код. Этот байт-код будет выглядеть посложнее, чем мы с вами видели, потому что функции, которые вы будете писать, могут быть сложнее, содержать всевозможные циклы, условия и так далее. И будут использоваться соответствующие инструкции.\n",
    "\n",
    "Все эти инструкции задокументированы, их можно посмотреть как в исходном коде Python, так и в документации. Ну, и можно посмотреть на них сейчас, есть модуль в стандартной библиотеке `opcode`, который мы можем заимпортировать и напечатать, например, на экран `opmap`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "b4d07e58",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'POP_TOP': 1, 'ROT_TWO': 2, 'ROT_THREE': 3, 'DUP_TOP': 4, 'DUP_TOP_TWO': 5, 'ROT_FOUR': 6, 'NOP': 9, 'UNARY_POSITIVE': 10, 'UNARY_NEGATIVE': 11, 'UNARY_NOT': 12, 'UNARY_INVERT': 15, 'BINARY_MATRIX_MULTIPLY': 16, 'INPLACE_MATRIX_MULTIPLY': 17, 'BINARY_POWER': 19, 'BINARY_MULTIPLY': 20, 'BINARY_MODULO': 22, 'BINARY_ADD': 23, 'BINARY_SUBTRACT': 24, 'BINARY_SUBSCR': 25, 'BINARY_FLOOR_DIVIDE': 26, 'BINARY_TRUE_DIVIDE': 27, 'INPLACE_FLOOR_DIVIDE': 28, 'INPLACE_TRUE_DIVIDE': 29, 'GET_AITER': 50, 'GET_ANEXT': 51, 'BEFORE_ASYNC_WITH': 52, 'BEGIN_FINALLY': 53, 'END_ASYNC_FOR': 54, 'INPLACE_ADD': 55, 'INPLACE_SUBTRACT': 56, 'INPLACE_MULTIPLY': 57, 'INPLACE_MODULO': 59, 'STORE_SUBSCR': 60, 'DELETE_SUBSCR': 61, 'BINARY_LSHIFT': 62, 'BINARY_RSHIFT': 63, 'BINARY_AND': 64, 'BINARY_XOR': 65, 'BINARY_OR': 66, 'INPLACE_POWER': 67, 'GET_ITER': 68, 'GET_YIELD_FROM_ITER': 69, 'PRINT_EXPR': 70, 'LOAD_BUILD_CLASS': 71, 'YIELD_FROM': 72, 'GET_AWAITABLE': 73, 'INPLACE_LSHIFT': 75, 'INPLACE_RSHIFT': 76, 'INPLACE_AND': 77, 'INPLACE_XOR': 78, 'INPLACE_OR': 79, 'WITH_CLEANUP_START': 81, 'WITH_CLEANUP_FINISH': 82, 'RETURN_VALUE': 83, 'IMPORT_STAR': 84, 'SETUP_ANNOTATIONS': 85, 'YIELD_VALUE': 86, 'POP_BLOCK': 87, 'END_FINALLY': 88, 'POP_EXCEPT': 89, 'STORE_NAME': 90, 'DELETE_NAME': 91, 'UNPACK_SEQUENCE': 92, 'FOR_ITER': 93, 'UNPACK_EX': 94, 'STORE_ATTR': 95, 'DELETE_ATTR': 96, 'STORE_GLOBAL': 97, 'DELETE_GLOBAL': 98, 'LOAD_CONST': 100, 'LOAD_NAME': 101, 'BUILD_TUPLE': 102, 'BUILD_LIST': 103, 'BUILD_SET': 104, 'BUILD_MAP': 105, 'LOAD_ATTR': 106, 'COMPARE_OP': 107, 'IMPORT_NAME': 108, 'IMPORT_FROM': 109, 'JUMP_FORWARD': 110, 'JUMP_IF_FALSE_OR_POP': 111, 'JUMP_IF_TRUE_OR_POP': 112, 'JUMP_ABSOLUTE': 113, 'POP_JUMP_IF_FALSE': 114, 'POP_JUMP_IF_TRUE': 115, 'LOAD_GLOBAL': 116, 'SETUP_FINALLY': 122, 'LOAD_FAST': 124, 'STORE_FAST': 125, 'DELETE_FAST': 126, 'RAISE_VARARGS': 130, 'CALL_FUNCTION': 131, 'MAKE_FUNCTION': 132, 'BUILD_SLICE': 133, 'LOAD_CLOSURE': 135, 'LOAD_DEREF': 136, 'STORE_DEREF': 137, 'DELETE_DEREF': 138, 'CALL_FUNCTION_KW': 141, 'CALL_FUNCTION_EX': 142, 'SETUP_WITH': 143, 'LIST_APPEND': 145, 'SET_ADD': 146, 'MAP_ADD': 147, 'LOAD_CLASSDEREF': 148, 'EXTENDED_ARG': 144, 'BUILD_LIST_UNPACK': 149, 'BUILD_MAP_UNPACK': 150, 'BUILD_MAP_UNPACK_WITH_CALL': 151, 'BUILD_TUPLE_UNPACK': 152, 'BUILD_SET_UNPACK': 153, 'SETUP_ASYNC_WITH': 154, 'FORMAT_VALUE': 155, 'BUILD_CONST_KEY_MAP': 156, 'BUILD_STRING': 157, 'BUILD_TUPLE_UNPACK_WITH_CALL': 158, 'LOAD_METHOD': 160, 'CALL_METHOD': 161, 'CALL_FINALLY': 162, 'POP_FINALLY': 163}\n"
     ]
    }
   ],
   "source": [
    "from opcode import opmap\n",
    "\n",
    "print(opmap)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a86677b4",
   "metadata": {},
   "source": [
    "Мы видим, что на самом деле инструкций существует очень много. И, так или иначе, они в определенный момент будут задействованы. \n",
    "\n",
    "Что хотелось бы отметить? Знание о том, что ваш код компилируется в байт-код, может оказаться в определенный момент очень полезным. И, как минимум, вы не будете бояться того, что Python автоматически создает скомпилированные байт-код файлики в ваших директориях.\n",
    "\n",
    "Мы посмотрели на то, как 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
}
