су управления зависимостями Python
1
, который можно найти на
dbader .org
.
Ключевые выводы
Виртуальные среды отделяют зависимости одних ваших проектов от
других. Они помогают избегать конфликтов версий между пакетами
и различными версиями среды выполнения Python.
1
См.
https://dbader .org/products/managing-python-dependencies/?utm_source=python-tricks-
book&utm_medium=pdf&utm_campaign=pytricks-book
8 .3 . По ту сторону байткода 275
В качестве практической рекомендации: все ваши проекты Python
должны использовать виртуальные среды, в которых будут храниться
их зависимости. Это поможет избежать головной боли в будущем.
8 .3 . По ту сторону байткода
Когда интерпретатор СPython исполняет вашу программу, он сначала ее
транслирует в последовательность байткодовых инструкций. Байткод —
это промежуточный язык для виртуальной машины Python, который
используется в качестве оптимизации производительности.
Вместо того чтобы непосредственно исполнить человекочитаемый исход-
ный код, в Python используются компактные цифровые коды, константы
и ссылки, которые представляют результат лексического и семантическо-
го анализа, выполняемого компилятором.
Это экономит время и оперативную память для повторных исполнений
программ или частей программ. Например, байткод, который получается
в результате этого шага компиляции, кэшируется на диске в файлах
.pyc
и
.pyo
, чтобы во второй раз исполнение того же самого файла Python
проходило быстрее.
Все это абсолютно прозрачно для программиста. Вам не нужно знать о том,
что происходит в этот промежуточный шаг трансляции или как виртуаль-
ная машина Python работает с байткодом. На самом деле формат байткода
считается деталью реализации, и не гарантируется, что он останется ста-
бильным или совместимым между различными версиями Python.
И все же, я убежден, что возможность увидеть, как делается колбаса,
и заглянуть по ту сторону абстракций, обеспечиваемых интерпретатором
Python, весьма информативна. Понимание, по крайней мере, некоторых
внутренних механизмов поможет вам писать более производительный
код (когда производительность имеет значение). И это также обеспечит
массу удовольствия.
В качестве подопытного образца давайте возьмем простую функцию
greet()
, с которой можно поэкспериментировать и которую можно ис-
пользовать, чтобы разобраться в байткоде Python:
276 Глава 8 • Питоновские методы повышения производительности
def greet(name):
return 'Привет, ' + name + '!'
>>> greet('Гвидо')
'Привет, Гвидо!'
Если помните, я уже отмечал, что Python сначала транслирует наш ис-
ходный код в промежуточный язык, прежде чем он его «выполнит». Так
вот, если это правда, то мы должны увидеть результаты этого шага ком-
пиляции. И мы можем.
Каждая функция имеет атрибут
__code__
(в Python 3), который мы мо-
жем использовать, чтобы получить инструкции виртуальной машины,
константы и переменные, используемые нашей функцией
greet
:
>>> greet.__code__.co_code
b'dx01|x00x17x00dx02x17x00Sx00'
>>> greet.__code__.co_consts
(None, 'Привет, ', '!')
>>> greet.__code__.co_varnames
('name',)
Вы видите, что
co_consts
содержит части строки приветствия, которую
собирает наша функция. Константы и код хранятся отдельно, чтобы сэко-
номить пространство памяти. Константы... как бы сказать… константны,
то есть они не подлежат изменению и используются попеременно в раз-
ных местах.
Поэтому вместо того, чтобы повторять фактические постоянные вели-
чины в потоке команд
co_code
, Python хранит константы отдельно в по-
исковой таблице. Поток команд затем может ссылаться на константу
по индексу в поисковой таблице. То же самое верно и для переменных,
хранящихся в поле
co_varnames
.
Надеюсь, что этот общий принцип начинает проясняться. Но рассмотре-
ние потока команд
co_code
по-прежнему заставляет меня чувствовать
себя нехорошо. Этот промежуточный язык явно предназначен для того,
чтобы с ним было легко работать виртуальной машине Python, а не
людям. В конце концов, для это существует текстоориенированный ис-
ходный код.
8 .3 . По ту сторону байткода 277
Разработчики, работающие над СPython, тоже это поняли. Поэтому они
дали нам еще один инструмент, который называется дизассемблером,
чтобы сделать инспектирование байтокда легче.
Дизассемблер байткода Python располагается в модуле
dis
, который яв-
ляется составной частью стандартной библиотеки. Поэтому мы можем
его просто импортировать и вызвать
dis.dis()
с функцией
greet
в каче-
стве аргумента, чтобы получить более удобочитаемое представление о ее
байткоде:
>>> import dis
>>> dis.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 является реализацией такой стековой
278 Глава 8 • Питоновские методы повышения производительности
машины. Если вся эта штука названа в честь стека, то вы можете себе
представить, какую важную роль играет эта структура данных.
Между прочим, здесь я касаюсь лишь верхов. Если вы интересуетесь
этой темой, то дальше найдете рекомендации для дальнейшего изучения.
Более глубокое ознакомление с принципами работы виртуальных машин
открывает глаза на многое (а еще это весело).
Самое интересное относительно стека как абстрактной структуры данных
состоит в том, что на минимальном уровне он поддерживает всего две опе-
рации: вталкивание (push) и выталкивание (pop). Вталкивание добавляет
значение на вершину стека, а выталкивание удаляет и возвращает самое
верхнее значение. В отличие от массива, в стеке отсутствует способ полу-
чить доступ к элементам «ниже» верхнего уровня.
Просто поразительно, что такая простая структура данных пользуется
столь большой популярностью. Однако я увлекся.
Давайте предположим, что вначале стек пустой. После того как первые два
кода операции были исполнены, содержимое стека виртуальной машины
будет выглядеть следующим образом (0 — это самый верхний элемент):
0: 'Гвидо' (содержимое "name")
1: 'Привет, '
Инструкция
BINARY_ADD
выталкивает два строковых значения из стека,
конкатенирует их, а затем вталкивает результат снова в стек:
0: 'Привет, Гвидо'
Затем идет еще одна инструкция
LOAD_CONST
, которая помещает в стек
строку с восклицательным знаком:
0: '!'
1: 'Привет, Гвидо'
Следующий код операции
BINARY_ADD
снова объединяет два значения,
чтобы сгенерировать заключительную приветственную строку:
0: 'Привет, Гвидо!'
8 .3 . По ту сторону байткода 279
Последняя байткодовая инструкция —
RETURN_VALUE
, которая сообщает
виртуальной машине следующее: то, что в настоящее время находится на
вершине стека, является возвращаемым значением этой функции, и по-
этому оно может быть передано источнику вызова.
И — вуаля! — мы только что проследили за тем, как наша функция
greet()
была исполнена на внутреннем уровне виртуальной машиной Python.
Разве не круто?
Можно еще много рассказывать о виртуальных машинах, но эта книга по-
священа не им. Однако если эта захватывающая тема вас заинтриговала,
то настоятельно рекомендую заняться ее изучением.
Можно получить массу удовольствия от создания и определения своих
собственных байткодовых языков и построения для них небольших экс-
периментов с использованием виртуальной машины. По этой теме я по-
рекомендовал бы книгу Проектирование компиляторов: виртуальные
машины (Compiler Design: Virtual Machines, Wilhelm and Seidl).
Ключевые выводы
СPython исполняет программы, сначала транслируя их в промежу-
точный байткод, а затем выполняя байткод на виртуальной машине
со стековой архитектурой.
Вы можете использовать встроенный модуль
dis
, чтобы заглянуть за
кулисы и проинспектировать байткод.
Займитесь плотнее виртуальными машинами — оно того стоит.
9
Итоги
Примите поздравления — вы прошли весь путь до самого конца! Самое
время похлопать себя по плечу, поскольку большинство людей покупают
книгу и даже ее не открывают или не доходят до конца первой главы.
Но теперь, когда вы прочитали эту книгу, начинается настоящая работа.
Ведь, как известно, читать и делать — это две большие разницы. Возьми-
те новые навыки и идиомы, которые вы узнали из этой книги, и начните
их использовать на практике. Не позвольте этой книге стать просто оче-
редной прочитанной книгой по программированию.
Что, если с этого момента вы начнете усеивать свой программный код рас-
ширенными функциональными возможностями языка Python? Изящное
и чистое выражение-генератор тут, элегантное применение инструкции
with
там…
В мгновение ока вы привлечете внимание своих товарищей — и по хо-
рошему поводу, если вы все сделаете правильно. Когда вы наработаете
небольшой опыт, у вас не будет никаких затруднений в правильном
применении этих продвинутых функциональных средств Python и ис-
пользовании их только там, где они имеют смысл и помогают делать про-
граммный код выразительнее.
И поверьте, через некоторое время ваши коллеги подхватят тренд. Если
они задают вам вопросы, делитесь с ними полезными знаниями. Подтя-
гивайте окружающих и помогайте им. Возможно, через пару недель вы
9 .1 . Бесплатные еженедельные советы для разработчиков на Python 281
даже устроите коллегам небольшую презентацию по теме «написания
чистого Python». Не стесняйтесь использовать мои примеры из этой
книги.
Для разработчика Python есть разница между выполнением отличной
работы и тем, как выполнение отличной работы смотрится со стороны.
Не бойтесь выделиться. Если вы поделитесь своими навыками и вновь
приобретенными знаниями с окружающими, то ваша карьера от этого
только выиграет.
В своей работе и проектах я следую тем же самым принципам. И поэто-
му я всегда ищу способы улучшить эту книгу и другие мои обучающие
материалы по языку Python. Если вы хотите сообщить мне об ошибке,
у вас просто есть вопрос или же вы хотите дать какой-то конструктивный
отзыв, то пишите мне по адресу
mail@dbader .org
.
Успешного программирования на Python!
Do'stlaringiz bilan baham: |