def greet(name):
return 'Привет, ' + name + '!'Байткод в Python

# пример для проведения декомпиляции
x = 30
y = 62
z = x + y
# python3.8 -m dis test.py
'''
1 0 LOAD_CONST 0 (30)
2 STORE_NAME 0 (x)
2 4 LOAD_CONST 1 (62)
6 STORE_NAME 1 (y)
3 8 LOAD_NAME 0 (x)
10 LOAD_NAME 1 (y)
12 BINARY_ADD
14 STORE_NAME 2 (z)
16 LOAD_CONST 2 (None)
18 RETURN_VALUE
'''Когда интерпретатор СPython исполняет программу, он сначала ее транслирует в последовательность байткодовых инструкций.
Байткод — это промежуточный язык для виртуальной машины Python, который используется в качестве оптимизации производительности.
Байткод, который получается в результате этого шага компиляции, кэшируется на диске в файлах .pyc и .pyo, чтобы во второй раз исполнение того же самого файла Python проходило быстрее.
greet('Гвидо')'Привет, Гвидо!'
Каждая функция имеет атрибут __code__, который мы можем использовать, чтобы получить инструкции виртуальной машины, константы и переменные, используемые нашей функцией greet:
greet.__code__.co_codeb'd\x01|\x00\x17\x00d\x02\x17\x00S\x00'
greet.__code__.co_consts
# https://docs.python.org/3/library/inspect.html#types-and-members(None, 'Привет, ', '!')
greet.__code__.co_varnames('name',)
co_consts содержит части строки приветствия, которую собирает наша функция. Константы и код хранятся отдельно, чтобы сэкономить пространство памяти.
Python хранит константы отдельно в поисковой таблице. Поток команд затем может ссылаться на константу по индексу в поисковой таблице.
Дизассемблер байткода Python располагается в модуле dis (документация), который является составной частью стандартной библиотеки. Поэтому мы можем его просто импортировать и вызвать dis.dis() с функцией greet в качестве аргумента, чтобы получить более удобочитаемое представление о ее байткоде:
import disdis.dis(greet) 2 0 LOAD_CONST 1 ('Привет, ')
2 LOAD_FAST 0 (name)
4 BINARY_ADD
6 LOAD_CONST 2 ('!')
8 BINARY_ADD
10 RETURN_VALUE
Главное, что сделал дизассемблер, было разбиение потока команд и назначение каждому находящемуся в нем коду операции человекочитаемого имени, как, например, LOAD_CONST.
Ссылки на константы и переменные теперь чередуются с байткодом и выведены полностью, чтобы уберечь нас от мозговой гимнастики относительно поиска по таблице co_const или co_varnames.
Глядя на человекочитаемые коды операций, мы начинаем понимать, как Python представляет и исполняет выражение 'Привет, ' + name + '!' в исходной функции greet().
Сначала он извлекает константу в индексе 1 ('Привет, ') и помещает ее в стек. Затем он загружает содержимое переменной name и также помещает ее в стек.
Стек является структурой данных, которая используется в качестве внутренней рабочей памяти виртуальной машины. Существуют разные классы виртуальных машин, и один из них называется стековой машиной. Виртуальная машина Python является реализацией такой стековой машины.
Самое интересное относительно стека как абстрактной структуры данных состоит в том, что на минимальном уровне он поддерживает всего две операции: вталкивание (push) и выталкивание (pop).
Вталкивание добавляет значение на вершину стека, а выталкивание удаляет и возвращает самое верхнее значение. В отличие от массива, в стеке отсутствует способ получить доступ к элементам «ниже» верхнего уровня.
Давайте предположим, что вначале стек пустой. После того как первые два кода операции были исполнены, содержимое стека виртуальной машины будет выглядеть следующим образом (0 — это самый верхний элемент):
0: 'Гвидо' (содержимое "name")
1: 'Привет, '
Инструкция BINARY_ADD выталкивает два строковых значения из стека, конкатенирует их, а затем вталкивает результат снова в стек:
0: 'Привет, Гвидо'
Затем идет еще одна инструкция LOAD_CONST, которая помещает в стек строку с восклицательным знаком:
0: '!'
1: 'Привет, Гвидо'
Следующий код операции BINARY_ADD снова объединяет два значения, чтобы сгенерировать заключительную приветственную строку:
0: 'Привет, Гвидо!'
Последняя байткодовая инструкция — RETURN_VALUE, которая сообщает виртуальной машине следующее: то, что в настоящее время находится на вершине стека, является возвращаемым значением этой функции, и поэтому оно может быть передано источнику вызова.