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