Интерпретатор python что это
Как работает Python: интерпретатор, байт-код, PVM
P ython — интерпретируемый язык программирования. Он не конвертирует свой код в машинный, который понимает железо (в отличие от С и С++). Вместо этого, Python-интерпретатор переводит код программы в байт-код, который запускается на виртуальной машине Python (PVM). Давайте рассмотрим подробнее, как это работает на примере самой популярной реализации интерпретатора — CPython.
Интерпретатор — это программа, которая конвертирует ваши инструкции, написанные на Python, в байт-код и выполняет их. По сути интерпретатор — это программный слой между вашим исходным кодом и железом.
Существует 2 типа интерпретаторов:
Кроме этого, у интерпретатора CPython есть особенность — он может работать в режиме диалога (REPL — read-eval-print loop). Интерпретатор считывает законченную конструкцию языка, выполняет её, печатает результаты и переходит к ожиданию ввода пользователем следующей конструкции.
Как CPython выполняет программы
Интерпретатор «Питона» выполняет любую программу поэтапно.
Этап #1. Инициализация
После запуска вашей программы, Python-интерпретатор читает код, проверяет форматирование и синтаксис. При обнаружении ошибки он незамедлительно останавливается и показывает сообщение об ошибке.
Помимо этого, происходит ряд подготовительных процессов:
Этап #2. Компиляция
Интерпретатор транслирует (переводит) исходные инструкции вашей программы в байт-код (низкоуровневое, платформонезависимое представление исходного текста). Такая трансляция необходима в первую очередь для повышения скорости — байт-код выполняется в разы быстрее, чем исходные инструкции.
Этап #3. Выполнения
Как только байт-код скомпилирован, он отправляется на виртуальную машину Python (PVM). Здесь выполняется байт-код на PVM. Если во время этого выполнения возникает ошибка, то выполнение останавливается с сообщением об ошибке.
PVM является частью Python-интерпретатора. По сути это просто большой цикл, который выполняет перебор инструкций в байт-коде и выполняет соответствующие им операции.
Альтернативы CPython
CPython является стандартной реализацией, но существуют и другие реализации, созданные для специфических целей и задач.
Jython
Основная цель данный реализации — тесная интеграция с языком Java. Работает следующим образом:
Jython позволить Python-программам управлять Java-приложениями. Во время выполнения такая программа ведет себя точно так же, как настоящая программа на языке Java.
IronPython
PyPy — это интерпретатор Python, написанный на Python (если быть точнее, то на RPython).
Особенностью PyPy является использование трассирующего JIT-компилятора (just-in-time), который на лету транслирует некоторые элементы в машинный код. Благодаря этому, при выполнении некоторых операций PyPy обгоняет CPython в несколько раз. Но плата за такую производительность — более высокое потребление памяти.
Разборки в террариуме. Изучаем виды интерпретаторов Python
Содержание статьи
На самом деле Python — это далеко не один конкретный интерпретатор. Действительно, чаще всего мы имеем дело с так называемым CPython, который можно назвать эталонной реализацией (reference implementation) — интерпретатором, на который все остальные должны равняться. Это означает, что CPython максимально полно и быстро реализует те вещи, которые уже прописаны и добавляются с течением времени в стандарт языка, содержащийся во всякого рода спеках и PEP’ах. И именно эта реализация находится под пристальным вниманием «великодушного пожизненного диктатора» (это реально существующий термин, не веришь — глянь в Википедии) и создателя языка Гвидо ван Россума.
Но все сказанное вовсе не значит, что нельзя просто так взять и воплотить свою собственную реализацию описанных стандартов, равно как ничто не мешает написать, к примеру, свой компилятор для C++. Собственно, довольно большое число разработчиков именно так и сделали. О том, что у них получилось, я и хочу рассказать.
Чтобы понять Python, надо понять Python
Одна из самых известных альтернативных реализаций Python — это PyPy (бывший Psyco), который часто называют питоном, написанным на питоне. У всех, кто слышит подобное определение, возникает закономерный вопрос — как то, что написано на том же языке, может быть быстрее, чем сам язык? Но мы выше уже сошлись на том, что Python — это общее название группы стандартов, а не конкретной реализации. В случае с PyPy мы имеем дело не с CPython, а с так называемым RPython — это даже не совсем диалект языка, это, скорее, платформа или фреймворк для написания своих собственных интерпретаторов.
В столкновениях поклонников лагерей Python и Ruby один из аргументов питонистов — это то, что разработчики, которых не устраивала скорость работы языка Ruby, написали якобы более быструю реализацию на RPython. Получился вполне интересный проект под названием Topaz.
RPython привносит немного низкоуровневой магии, которая позволяет при правильном подборе ингредиентов (прямые руки обязательны, глаз тритона — нет) добавить разные полезные плюшки в произвольный интерпретируемый язык (необязательно Python) — например, ускорение за счет JIT. Если хочется больше подробностей об этой штуке, а также если ты сейчас подумал что‑то вроде «а как же LLVM?» — добро пожаловать в FAQ.
Общая мысль заключается в том, что RPython — слишком специфическая реализация для того, чтобы прямо на нем писать боевые программы. Проще и удобнее взять PyPy, даром что он полностью совместим с CPython 2.7 и 3.3 в плане поддерживаемых стандартов. Правда, в силу специфики внутреннего устройства на нем пока что трудновато заставить работать те библиотеки для эталонной реализации, которые используют компилируемые си‑модули. Но, к примеру, Django уже вполне поддерживается, причем во многих случаях бегает быстрее, чем на CPython.
Еще в качестве одной из полезных особенностей PyPy можно назвать его модульность и расширяемость. Например, в него встроена поддержка sandboxing’а — можно запускать «опасный» произвольный код внутри виртуального окружения, даже с эмуляцией файловой системы и запретом на внешние вызовы вроде создания сокетов. А еще в последнее время довольно активно развивается проект PyPy-STM, который позволяет заменить встроенную поддержку многопоточности (ту самую, с GIL и прочими нелюбимыми вещами) на реализацию, работающую через Software Transactional Memory, — то есть с абсолютно другим механизмом разруливания конкурентного доступа.
Это не совсем относится к теме статьи, но если тебя заинтересовала информация об RPython — крайне рекомендую взглянуть еще на один проект по «более скоростному запуску» Python. Он называется Nuitka и позиционирует себя как «компилятор Python в C++». Правда, любопытно?
Нижний уровень
Кроме RPython + PyPy, есть и более простой способ ускорить выполнение кода на Python — выбрать один из оптимизирующих компиляторов, который расширяет стандарты языка, добавляя более строгую статическую типизацию и прочие низкоуровневые вещи. Как я выше упомянул, RPython (даже притом, что названное определение к нему тоже относится) является слишком специфичным инструментом для конкретных задач, но есть проект и более общего применения — cython.
Его подход заключается в добавлении фактически нового языка, являющегося чем‑то промежуточным между C и Python (за основу был взят проект Pyrex, который практически не развивается с 2010 года). Обычно исходные файлы с кодом на этом языке имеют расширение pyx, а при натравлении на них компилятора превращаются во вполне обычные C-модули, которые можно тут же импортировать и использовать.
Код с декларацией типов будет выглядеть как‑то так:
В простейшем варианте для такого модуля пишется setup.py примерно с таким содержанием:
И да, это фактически все, больше ничего в общем случае делать не надо.
Кстати, если кто‑то сейчас начнет возмущаться, что все типы давно уже есть в Numpy и не надо придумывать новый язык, — рекомендую почитать вот эту ссылку. Суть в том, что разработчики проекта активно работают над совместным использованием и того и другого, что позволяет на некоторых задачах получить довольно серьезный прирост производительности.
Змея в коробке
Так уж вышло, что упомянутый автор и BDFL оригинальной реализации Python Гвидо ван Россум сейчас работает в Dropbox, где целая команда под его началом трудится над свежим высокоскоростным интерпретатором Python, который на сей раз называется Pyston. Помимо тысячной версии искажения названия нежно нами любимого языка, он может похвастаться тем, что работает на LLVM с использованием самых современных течений в реализации JIT-компиляции.
Пока что эта реализация находится довольно в зачаточном состоянии, но, по слухам, вполне себе используется для конкретных внутренних проектов в Dropbox, а также, по уверениям разработчиков, должна сделать серьезный шаг вперед с точки зрения производительности (даже по сравнению с CPython). Но главный интерес она может представлять в том, что это своеобразный «лабораторный полигон для испытаний», на котором разработчики играют с различными технологиями — например, там в качестве подключаемого плагина можно использовать альтернативу GIL под названием GRWL или Global Read-Write Lock, которая якобы решает часть старых добрых проблем своего прародителя. Суть в том, что, в отличие от блокирования всего и вся (то есть, грубо говоря, обычного мьютекса, как оно устроено в GIL), он вводит разделение захватных операций на чтение и запись, что позволяет‑таки нескольким потокам получать одновременный доступ к данным, не портя их. Еще можно упомянуть так называемый OSR — On-Stack Replacement, который является своеобразной внутренней магией для запуска «тяжелых» методов (похожая штука используется в JavaScript). Такие дела.
Виртуальная реальность
Jython
Jython — не путать с JPython, похожим проектом, но развивающимся довольно вяло! — реализация Python, позволяющая в коде на Python вовсю и с удовольствием пользоваться почти всеми примитивами Java и ощутить преимущества JVM по максимуму. Выглядит это приблизительно так (пример из стандартной документации):
Интерпретатор Python: о чём думает змея? (часть I-III)
Данная серия статей рассчитана на тех, кто умеет писать на python в целом, но плохо представляет как этот язык устроен изнутри. Собственно, как и я три месяца назад.
Небольшой дисклеймер: свой рассказ я буду вести на примере интерпретатора python 2.7. Всё, о чем пойдёт речь далее, можно повторить и на python 3.x с поправкой на некоторые различия в синтаксисе и именование некоторых функций.
Часть I. Слушай Питон, а что у тебя внутри?
Начнём с немного (на самом деле, с сильно) высокоуровневого взгляда на то, что же из себя представляет наша любимая змея. Что происходит, когда вы набираете строку подобную этой в интерактивном интерпретаторе?
Ваш палец падает на enter и питон инициирует 4 следующих процесса: лексический анализ, парсинг, компиляцию и непосредственно интерпретацию. Лексический анализ – это процесс разбора набранной вами строки кода в определенную последовательность символов, называемых токенами. Далее парсер на основе этих токенов генерирует структуру, которая отображает взаимоотношения между входящими в неё элементами (в данном случае, структура это абстрактное синтаксическое древо или АСД). Далее, используя АСД, компилятор создаёт один или несколько объектных модулей и передаёт их в интерпретатор для непосредственного выполнения.
Я не буду углубляться в темы лексического анализа, парсинга и компиляции, в основном потому, что сам не имею о них ни малейшего представления. Вместо этого, давайте лучше представим, что умные люди сделали всё как надо и данные этапы в питоновском интерпретаторе отрабатывают без ошибок. Представили? Двигаем дальше.
Прежде чем перейти к объектным модулям (или объектам кода, или объектным файлам), следует кое-что прояснить. В данной серии статей мы будем говорить об объектах функций, объектных модулях и байткоде – всё это совершенно разные, хоть и некоторым образом связанные между собой понятия. Хотя нам и необязательно знать, что такое объекты функций для понимания интерпретатора, но я всё же хотел бы остановить на них ваше внимание. Не говоря уже о том, что они попросту крутые.
Объекты функций или функции, как объекты
Если это не первая ваша статья о программировании на питоне, вы должны быть наслышаны о неких «объектах функций». Это именно о них люди с умным видом рассуждают в контексте разговоров о «функциях, как объектах первого класса» и «наличии функций первого класса в питоне». Рассмотрим следующий пример:
Выражение «функции – это объекты первого класса» означает, что функции – это объекты первого класса, в том смысле, в коем и списки – это объекты, и экземпляр класса MyObject – объект. И так как foo это объект, он имеет значимость сам по себе, безотносительно вызова его, как функции (то есть, foo и foo() — это разные вещи). Мы можем передать foo в другую функцию в качестве аргумента, можем переназначить её на новое имя ( other_function = foo ). С функциями первого класса можно делать, что угодно и они всё стерпят.
Часть II. Объектные модули
На данном этапе we need to go deeper, чтобы узнать, что объект функции в свою очередь содержит объект кода:
Как видно из приведённого листинга, объектный модуль является атрибутом объекта функции (у которого есть и множество других атрибутов, но в данном случае особого интереса они не представляют в силу простоты foo ).
Объектный модуль генерируется питоновским компилятором и далее передаётся интерпретатору. Модуль содержит всю необходимую для выполнения информацию. Давайте посмотрим на его атрибуты:
Их, как видите, немало, поэтому все рассматривать не будем, для примера остановимся на трёх наиболее понятных:
Атрибуты выглядят довольно интуитивно:
co_varnames – имена переменных
co_consts – значения, о которых знает функция
co_argcount – количество аргументов, которые функция принимает
Всё это весьма познавательно, но выглядит несколько черезчур высокоуровнево для нашей темы, не правда ли? Где же инструкции интерпретатору для непосредственного выполнения нашего модуля? А такие инструкции есть и представлены они байткодом. Последний также является атрибутом объектного модуля:
Что за неведомая байтовая фигня, спросите вы?
Часть III. Байткод
Вы наверное и сами понимаете, но я, на всякий случай, озвучу – «байткод» и «объект кода» это разные вещи: первый является атрибутом второго, среди многих других (см. часть 2). Атрибут называется co_code и содержит все необходимые инструкции для выполнения интерпретатором.
Что же из себя представляет этот байткод? Как следует из названия, это просто последовательность байтов. При выводе в консоль выглядит она достаточно бредово, поэтому давайте приведём её к числовой последовательности, пропустив через ord :
Таким образом мы получили числовое представление питоновского байткода. Интерпретатор пройдётся по каждому байту в последовательности и выполнит связанные с ним инструкции. Обратите внимание, что байткод сам по себе не содержит питоновских объектов, ссылок на объекты и т.п.
Байткод можно попытаться понять открыв файл интерпретатора CPython (ceval.c), но мы этого делать не будем. Точнее будем, но позже. Сейчас же пойдём простым путём и воспользуемся модулем dis из стандартной библиотеки.
Дизассемблируй это
Итак, давайте применим dis и снимем паранжу с нашего объектного модуля. Для этого воспользуемся функцией dis.dis :
Числа в первой колонке – это номера строк анализируемых исходников. Вторая колонка отражает смещение команд в байткоде: LOAD_CONST находится в позиции «0», STORE_FAST в позиции «3» и т.д. Третья колонка даёт байтовым инструкциям человекопонятные названия. Названия эти нужны только жалким людишкам нам, в интерпретаторе они не используются.
Это также объясняет, почему инструкция STORE_FAST находится на третьей позиции в байткоде: если где-то в байткоде есть аргумент, следующие два байта будут представлять этот аргумент. Корректная обработка таких ситуаций также ложится на плечи интерпретатора.
Как dis переводит байты (например, 100) в осмысленные имена (например, LOAD_CONST ) и наоборот? Подумайте, как бы вы сами организовали подобную систему? Если у вас появились мысли, вроде «ну, может там есть какой-то список с последовательным определением байтов» или «по-любому словарь с названиями инструкций в качестве ключей и байтами как значениями», поздравляю – вы абсолютно правы. Именно так всё и устроено. Сами определения происходят в файле opcode.py (можно также посмотреть заголовочный файл opcode.h), где вы сможете увидеть
полторы сотни подобных строк:
(Какой-то любитель комментариев заботливо оставил нам пояснения к инструкциям.)
Теперь мы имеем некоторое представление о том, чем является (и чем не является) байткод и как использовать dis для его анализа. В следующих частях мы рассмотрим, как питон может компилироваться в байткод, оставаясь при этом динамическим ЯП.
Введение в Python
Python представляет популярный высокоуровневый язык программирования, который предназначен для создания приложений различных типов. Это и веб-приложения, и игры, и настольные программы, и работа с базами данных. Довольно большое распространение питон получил в области машинного обучения и исследований искусственного интеллекта.
Основные особенности языка программирования Python:
Python также популярен не только в сфере обучения, но в написании конкретных программ в том числе коммерческого характера. В немалой степени поэтому для этого языка написано множество библиотек, которые мы можем использовать.
Кроме того, у данного языка программирования очень большое коммьюнити, в интернете можно найти по данному языку множество полезных материалов, примеров, получить квалифицированную помощь специалистов.
Для создания программ на Python нам потребуется интерпретатор. Для его установки перейдем на сайт https://www.python.org/ и на главной станице в секции Downloads найдем ссылку на загрузку последней версии языка:
Перейдем по ссылке к странице с описанием последней версии языка. Ближе к низу на ней можно найти список дистрибутивов для разных операционных систем. Выберем нужный нам пакет и загрузим его. Например, в моем случае это ОС Windows 64-х разрядная, поэтому я выбираю ссылку на пакет Windows x86-64 executable installer. После загрузки дистрибутива установим его.
Соответственно для MacOS можно выбрать пункт macOS 64-bit installer.
На ОС Windows при запуске инсталлятора запускает окно мастера установки:
Здесь мы можем задать путь, по которому будет устанавливаться интерпретатор. Оставим его по умолчанию, то есть C:\Users\[имя_пользователя]\AppData\Local\Programs\Python\Python36\.
Кроме того, в самом низу отметим флажок «Add Python 3.6 to PATH», чтобы добавить путь к интерпретатору в переменные среды.
После установки в меню Пуск на ОС Windows мы сможем найти иконки для доступа к разным утилитам питона:
Здесь утилита Python 3.7 (64-bit) представляет интерпретатор, в котором мы можем запустить скрипт. В файловой системе сам файл интерпретатора можно найти по пути, по которому производилась установка. На Windows по умолчанию это путь C:\Users\[имя_пользователя]\AppData\Local\Programs\Python\Python37, а сам интерпретатор представляет файл python.exe. На ОС Linux установка производится по пути /usr/local/bin/python3.7.
После установки интерпретатора, как было описано в прошлой теме, мы можем начать создавать приложения на Python. Итак, создадим первую простенькую программу.
Как было сказано в прошлой теме, программа интерпретатора, если при установке не был изменен адрес, по умолчанию устанавливается на Linux по пути usr/local/bin/python37, а на Windows по пути C:\Users\[имя_пользователя]\AppData\Local\Programs\Python\Python37\ и представляет файл под названием python.exe.
Запустим интерпретатор и введем в него следующую строку:
И консоль выведет строку «hello world»:
Для этой программы использовался метод print(), который выводит некоторую строку на консоль.
В реальности, как правило, программы определяются во внешних файлах-скриптах и затем передаются интерпретатору на выполнение. Поэтому создадим файл программы. Для этого на диске C или где-нибудь в другом месте файловой системы определим для скриптов папку python. А в этой папке создадим новый текстовый файл, который назовем hello.py. По умолчанию файлы с кодом на языке Python, как правило, имеют расширение py.
Откроем этот файл в любом текстовом редакторе и добавим в него следующий код:
name = input(«Введите имя: «)
name = input(«Введите имя: «) print(«Привет,», name)
Скрипт состоит из двух строк. Первая строка с помощью метода input() ожидает ввода пользователем своего имени. Введенное имя затем попадает в переменную name.
Вторая строка с помощью метода print() выводит приветствие вместе с введенным именем.
Теперь запустим командную строку/терминал и с помощью команды cd перейдем к папке, где находится файл с исходным кодом hello.py (например, в моем случае это папка C:\python). Далее вначале введем полный путь к интерпретатору, а затем полный путь к файлу скрипта:
К примеру, в моем случае в консоль надо будет вести:
Но если при установке была указана опция «Add Python 3.7 to PATH», то есть путь к интерпретатору Python был добавлен в переменные среды, то вместо полного пути к интерпретатору можно просто написать python:
Варианты с обоими способами запуска:
В итоге программа выведет приглашение к вводу имени, а затем приветствие.
В прошлой теме было описано создание простейшего скрипта на языке Python. Для создания скрипта использовался текстовый редактор. В моем случае это был Notepad++. Но есть и другой способ создания программ, который представляет использование различных интегрированных сред разработки или IDE.
IDE предоставляют нам текстовый редактор для набора кода, но в отличие от стандартных текстовых редакторов, IDE также обеспечивает полноценную подсветку синтаксиса, автодополнение или интеллектуальную подсказку кода, возможность тут же выполнить созданный скрипт, а также многое другое.
Правда, она имеет одно важное ограничение. А именно она доступна в двух основных вариантах: платный выпуск Professional и бесплатный Community. Многие базовые возможности доступны и в бесплатном выпуске Community. В то же время ряд возможностей, например, веб-разработка, доступны только в платном Professional.
В нашем случае воспользуемся бесплатным выпуском Community. Для этого перейдем на страницу загрузки и загрузим установочный файл PyCharm Community. После загрузки выполним его установку.
После завершения установки запустим программу. При первом запуске открывается начальное окно:
Создадим проект и для этого выберем пункт Create New Project.
Далее нам откроется окно для настройки проекта. В поле Location необходимо указать путь к проекту. В моем случае проект будет помещаться в папку HelloApp. Собственно название папки и будет названием проекта.
Следует отметить, что PyCharm позволяет разграничить настройки проектов. Так, по умолчанию выбрано поле New Environment Using, что позволяет установить версию интерпретатора для конкретного проекта. Затем все устанавливаемые дополнительные пакеты будут касаться только текущего проекта. Это удобно, если мы создаем несколько проектов, но каждый из которых рабоает с какой-то специфической версией интерпретатора. Но в качестве альтернативы мы также можем выбрать поле Existing Interpreter и задать путь к файлу интерпретатора глобально для всех проектов.
В реальности для первого простейшего приложения на PyCharm не имеет значения, как будет установлен интерпертатор. Однако данном же случае оставим выбранный по умолчанию флажок New Environment Using и под ним в поле Base Interpreter укажем путь к файлу интерпретатора, установка которого рассматривалась в первой теме.
И после установки всех путей нажмем на кнопку Create для создания проекта.
После этого будет создан пустой проект:
Теперь создадим простейшую программу. Для этого нажмем на название проекта правой кнопкой мыши и в появившемся контекстном меню выберем
Затем откроется окно, в котором надо будет указать название файла. Пусть файл называется hello:
В созданный файл введем следующие строки:
name = input(«Введите ваше имя: «)
Для запуска скрипта нажмем на него правой кнопкой мыши и в контекстном меню выберем Run ‘hello’ (либо перейдем в меню Run и там нажмем на подпункт Run. ):
После этого внизу IDE отобразится окно вывода, где надо будет ввести имя и где после этого будет выведено приветствие:
Одной из сред разработки, которая позволяет работать с Python, является Visual Studio. Преимуществом данной IDE по сравнению, скажем, с PyCharm, следует отметить прежде всего то, что в ее бесплатной редакции VS 2017 Community бесплатно доступны ряд функций и возможностей, которые в том же PyCharm доступны только в платной версии Professional Edition. Например, это веб-разработка, в том числе с помощью различных фреймворков. В то же время средства ля разработки на Python в Visual Studo доступны пока только в версии для Windows.
Итак, загрузим установочный файл Visual Studio 2017 Community по ссылке https://www.visualstudio.com/ru/thank-you-downloading-visual-studio/?sku=Community&rel=15. После запуска установочного файла выберем среди устанавливаемых опций Python:
Выбрав слева Python, в центральной части окна мы можем увидеть богатую палитру типов проектов, которые мы можем создавать для разработке на данном языке программирования. Это и веб-разработка, и машинное обучение, и проекты для работы с облаком, проекты настольных приложений и т.д. В данном же случае выберем в качестве типа проекта Python Application, то есть тип простых консольных приложений, и назовем новый проект HelloApp. Нажмем на кнопку OK, и Visual Studio создаст новый проект:
Справа в окне Solution Explorer (Обозреватель решений) можно увидеть структуру проекта. По умолчанию здесь мы можем увидеть следующие элементы:
Python Environments: здесь можно увидеть все используемые среды, в частности, здесь можно найти сведения о компиляторе, который используется.
References: в этот узел помещаются все внешние зависимости, которые используются текущим проектом
Search Paths: этот узел позволяет указать пути поиска для модулей Python
HelloApp.py: собственно файл Python с исходным кодом
По умолчанию в Visual Studio уже открыт файл HelloApp.py, но он пока пуст. Добавим в него следующую строку:
print(«Hello Python from Visual Studio!»)
И затем в панели инструментов нажмем на зеленую стрелочку для запуска:
В результате запуска отобразится консоль, которая выведет нужную строку: