что такое виртуальная машина python
Внутри виртуальной машины Python. Часть 1
Оглавление
Введение
Язык программирования Python существует уже довольно давно. Разработка первой версии была начата Гвидо Ван Россумом в 1989 году, и с тех пор язык вырос и стал один из самых популярных. Python используется в различных приложениях: начиная от графических интерфейсов и заканчивая приложениями для анализа данных.
Цель этой статьи — выйти за кулисы интерпретатора и предоставить концептуальный обзор того, как выполняется программа написанная на Python. В материале будет рассмотрен CPython, потому что на момент написания статьи он является наиболее популярной и основной реализацией Python.
Python и CPython используются в этом тексте как синонимы, но при любом упоминании Python имеется ввиду CPython (версия python, реализованная на C). К другим реализациям относится PyPy (python, реализованный в ограниченном подмножестве Python), Jython (реализация на Виртуальной машине Java) и т.д.
Мне нравится делить выполнение Python-программы на два или три основных этапах (указанных ниже), в зависимости от того, как вызывается интерпретатор. Эти этапы будут в разной степени охвачены в данной статье:
Этот материал предназначен для всех, кому интересно узнать, как работает виртуальная машина CPython. Предполагается, что пользователь уже знаком с python и понимает основы языка. При изучении строения виртуальной машины нам встретится значительное количество C-кода, поэтому пользователю, который имеет элементарное понимание языка C, будет легче разобраться в материале. И так, в основном, что потребуется для ознакомления с этим материалом: желание узнать больше о виртуальной машине CPython.
Эта статья представляет собой расширенную версию личных заметок, сделанных при исследовании внутренней работы интерпретатора. Существует много качественного материала в PyCon видео, школьных лекциях и данном блоге. Моя работа не была бы закончена без этих фантастических источников знаний.
В конце этой книги читатель будет в состоянии понять тонкости того, как интерпретатор Python выполняет вашу программу. Это включает в себя различные этапы выполнения программы и структуры данных, которые имеют решающее значение в программе. Для начала, мы рассмотрим с высоты птичьего полета то, что происходит при выполнении тривиальной программы, когда интерпретатору передаётся имя модуля в командной строке. Исполняемый код CPython может быть установлен из исходников, следуя Руководству Python разработчика.
В данной книге используется версия Python 3
Взгляд с высоты в 30 000 футов
В этой главе рассказывается о том, как интерпретатор выполняет Python программу. В последующих главах мы рассмотрим различные части этой «головоломки» и предоставим более подробное описание каждой части. Независимо от сложности программы, написанной на Python, данный процесс всегда одинаков. Прекрасное объяснение, данное Янивом Акниным в его серии статей о Python Internal, задаёт тему нашего обсуждения.
Рисунок 2.1: Поток во время выполнения исходного кода.
Исполняемый файл python — это обычная С-программа, поэтому при его вызове происходят процессы похожие на те, которые существуют, например, в ядре linux или простой программке «hello world». Потратьте минуту своего времени, чтобы понять: исполняемый файл python — это просто еще одна программа, которая запускает вашу собственную. Такие «отношения» существуют между языком Cи и ассемблером (или llvm). Стандартный процесс инициализации (который зависит от платформы, где происходит выполнение) запускается, когда вызывается исполняемый файл python с именем модуля в качестве аргумента.
Эта статье предполагает использование операционной системы на основе Unix, поэтому некоторые особенности могут отличаться на Windows.
Язык C во время запуска выполняет всю свою «магию» инициализации — загружает библиотеки, проверяет/устанавливает переменные среды, а после этого, основной метод исполняемого файла python запускается так же, как и любая другая C-программа. Пайтоновский main исполняемого файла находится в ./Programs/python.c и выполняет некоторую инициализацию (такую как создание копий аргументов командной строки программы, которые были переданы в модуль). Затем функция main вызывает функцию Py_Main, расположенную в ./Modules/main.c. Она обрабатывает процесс инициализации интерпретатора: анализирует аргументы командной строки, устанавливает флаги, читает переменные среды, выполняет хуки, занимается рандомизацией хеш-функций и т.д. Также вызывается Py_Initialize из pylifecycle.c, который обрабатывает инициализацию структур данных состояния интерпретатора и потока — это две очень важные структуры данных.
Рассмотрение объявлений структур данных интерпретатора и состояний потоков позволяет понять, зачем они нужны. Состояние интерпретатора и потока — это просто структуры с указателями на поля, которые содержат информацию, необходимую для выполнения программы. Данные состояния интерпретатора создаются через typedef (просто думайте об этом ключевом слове в C, как об определении типа, хотя это не совсем так). Код этой структуры приведён в листинге 2.1.
Листинг 2.1: Структура данных состояния интерпретатора
Любой, кто достаточно долго использовал язык программирования Python, может узнать несколько полей, упомянутых в этой структуре (sysdict, builtins, codec).
Листинг 2.2: Часть структуры данных состояния потока
Структуры данных интерпретатора и состояния потока обсуждаются более подробно в следующих главах. Процесс инициализации также устанавливает механизмы импорта, а также элементарный stdio.
Если вы уже просмотрели исходный код, то наверняка столкнулись с Py_INCREF и Py_DECREF. Это функции управления памятью, которые мы позже обсудим подробно. CPython управляет жизненным циклом объекта с помощью подсчета ссылок: всякий раз, когда создается новая ссылка на объект, значение увеличивается через Py_INCREF. Аналогично, когда ссылка выходит из области видимости, то счётчик уменьшается с помощью функции Py_DECREF.
Python предоставляет набор функций, которые можно использовать для изучения реальных объектов кода. Например, простая программа может быть скомпилирована в объект кода и дизассемблирована для получения opcode-ов, которые выполняются виртуальной машиной python. Это показано в листинге 2.3.
Листинг 2.3: Дизассемблирование функции в Python
Заголовочный файл ./Include/opcodes.h содержит полный список всех инструкций/опкодов для виртуальной машины Python. Opcode-ы довольно просты. Возьмем наш пример из листинга 2.3, который имеет набор из четырех инструкций. LOAD_FAST загружает значение своего аргумента (в данном случае x) в стек значений. Виртуальная машина python основана на стеке, поэтому значения для операций опкодов «достаются» из стека, а результаты вычислений помещаются обратно в стек, для дальнейшего использования другими опкодами. Затем BINARY_MULTIPLY извлекает два элемента из стека, выполняет двоичное перемножение обоих значений и помещает результат обратно в стек. Инструкция RETURN VALUE извлекает значение из стека, устанавливает возвращаемое значение для объекта в это значение и выходит из цикла интерпретатора. Если посмотреть на листинг 2.3, то ясно, что это довольно сильное упрощение.
Текущее объяснение работы цикла интерпретатора не учитывает ряд деталей, которые будут обсуждаться в последующих главах. Например, вот вопросы на которые мы не получили ответа:
И так, мы рассмотрели «на высоком уровне» описание процессов, которые происходят в исполняемом файле 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. Урок 17. Виртуальные окружения
Что такое виртуальное окружение и зачем оно нужно?
Во-первых : различные приложения могут использовать одну и туже библиотеку, но при этом требуемые версии могут отличаться.
Во-вторых : может возникнуть необходимость в том, чтобы запретить вносить изменения в приложение на уровне библиотек, т.е. вы установили приложение и хотите, чтобы оно работало независимо от того обновляются у вас библиотеки или нет. Как вы понимаете, если оно будет использовать библиотеки из глобального хранилища ( /usr/lib/pythonXX/site-packages ), то, со временем, могут возникнуть проблемы.
Для решения данных вопросов используется подход, основанный на построении виртуальных окружений – своего рода песочниц, в рамках которых запускается приложение со своими библиотеками, обновление и изменение которых не затронет другие приложение, использующие те же библиотеки.
ПО позволяющее создавать виртуальное окружение в Python
Программное обеспечение, которое позволяет создавать виртуальные окружения в Python можно разделить на те, что входят в стандартную библиотеку Python и не входят в нее. Сделаем краткий обзор доступных инструментов (хороший пост на эту тем есть на stackoverflow ).
Virtualenvwrapper – это обертка для virtualenv позволяющая хранить все изолированные окружения в одном месте, создавать их, копировать и удалять. Предоставляет удобный способ переключения между окружениями и возможность расширять функционал за счет plug-in ’ов.
virtualenv
Установка virtualenv
Virtualenv можно установить с использованием менеджера pip (ссылка на статью), либо скачать исходные коды проекта и установить приложение вручную.
Установка с использованием pip.
Для установки virtualenv откройте консоль и введите следующую команду:
Установка из исходного кода проекта.
В этом случае, вам нужно будет выполнить чуть большее количество действий.
Введите в консоли следующий набор команд:
X.X – это версия приложения, ее вам нужно знать заранее.
Создание виртуального окружения
Виртуальное окружение создается следующей командой:
PRG1 в данном случае – это имя окружения.
Активация виртуального окружения
Для активации виртуального окружения воспользуйтесь командой (для Linux ):
для Windows команда будет выглядеть так:
Команда source выполняет bash- скрипт без запуска второго bash- процесса.
Если команда выполнилась успешно, то вы увидите, что перед приглашением в командной строке появилась дополнительная надпись, совпадающая с именем виртуального окружения.
Если вы создадите виртуальное окружение с ключем –system-site-packages :
то в рамках окружения PRG1 вы будите иметь доступ к глобальному хранилищу пакетов:
Деактивация виртуального окружения
venv
Создание виртуального окружения
Для создания виртуального окружения с именем PRG2 с помощью venv выполните следующую команду:
Активация виртуального окружения
Активация виртуального окружения в Linux выполняется командой:
Деактивация виртуального окружения
Деактивация выполняется командой deactivate (работает как в Windows, так и в Linux )
Полезные ссылки
P.S.
Если вам интересна тема анализа данных, то мы рекомендуем ознакомиться с библиотекой Pandas. На нашем сайте вы можете найти вводные уроки по этой теме. Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.
Как работает Python?
Всем еще раз привет, сейчас расскажу о том, как работает Python, что такое интерпретатор, как работает компилятор и что такое байт-код, далее расскажу о виртуальной машине (PVM) и о производительности Python. Также о альтернативных реализациях интерпретатора.
После того, как вы установили себе Python, перейдем к теоретически-практической части и начнем с того что из себя представляет интерпретатор.
Интерпретатор
В зависимости от используемой версии Python сам интерпретатор может быть реализован как программа на языке C, как набор классов Java и в каком-либо другом виде, но об этом позже.
Запуск сценария в консоли
Давайте запустите в консоле интерпретатор:
Теперь он ожидает ввода комманд, введите туда следующую инструкцию:
ура, наша первая программа! 😀
Запуск сценария из файла
Создайте файл «test.py», с содержимым:
и выполните этот файл:
Вы увидите в консоли результат, поехали дальше!
Динамическая компиляция и байт-код
В следующий раз, когда вы запустите свою программу интерпретатор минует этап компиляции и отдаст на выполнение откомпилированный файл с расширением «.pyc». Однако, если вы изменили исходные тексты вашей программы, то снова произойдет этап компиляции в байт-код, так как Python автоматически следит за датой изменения файла с исходным кодом.
Если Python окажется не в состоянии записать файл с байт-кодом, например из-за отсутствия прав на запись на диск, то программа не пострадает, просто байт-код будет собран в памяти и при завершении программы оттуда удален.
Виртуальная машина Python (PVM)
Производительность
По этим причинам программы на Python не могут выполняться также быстро как на C/C++. Обход инструкций выполняет виртуальная система, а не микропроцессор, и чтобы выполнить байт-код, необходима дополнительная интерпретация, инструкции которой требуют большего времени, чем машинные инструкции микропроцессора.
В итоге, Python по производительности находится между традиционными компилирующими и традиционными интерпретирующими языками программирования.
Альтернативные реализации Python
То что было сказано выше о компиляторе и виртуальной машине, характерно для стандартной реализации Python, так называемой CPython (реализации на ANSI C). Однако также существует альтернативные реализации, такие как Jython и IronPython, о которых пойдет сейчас речь.
CPython
Это стандартная и оригинальная реализация Python, названа так, потому что написана на ANSI C. Именно ее мы установили, когда выбрали пакет ActivePython или установили из FreeBSD портов. Поскольку это эталонная реализация, она как правило работает быстрее, устойчивее и лучше, чем альтернативные реализации.
Jython
Цель Jython состоит в том, чтобы позволить программам на языке Python управлять Java-приложениями, точно также как CPython может управлять компонентами на языках C/C++. Эта реализация имеет беcшовную интеграцию с Java. Поскольку программный код на Python транслируется в байт-код Java, во время выполнения он ведет себя точно также, как настоящая программа на языке Java. Программы на Jython могут выступать в качестве апплетов и сервлетов, создавать графический интерфейс с использованием механизмов Java и т.д. Более того, Jython обеспечивает поддержку возможности импортировать и использовать Java-классы в программном коде Python.
Тем не менее, поскольку реализация Jython обеспечивает более низкую скорость выполнения и менее устойчива по сравнению с CPython, она представляет интерес скорее для разработчиков программ на языке Java, которым необходим язык сценариев в качестве интерфейса к Java-коду.
IronPython
Средства оптимизации скорости выполнения
Существуют и другие реализации, включая динамический компилятор Psyco и транслятор Shedskin C++, которые пытаются оптимизировать основную модель выполнения.
Динамический компилятор Psyco
Во время выполнения программы, Psyco собирает информацию о типах объектов, и затем эта информация используется для генерации высокоэффективного машинного кода, оптимизированного для объектов этого типа. После этого произведенный машинный код заменяет соответствующие участки байт-кода, тем самым увеличивается скорость выполнения.
В идеале некоторые участки программного кода под управление Psyco могут выполняться также быстро, как скомпилированный код на языке Си.
Psyco обеспечивает увеличение скорости от 2 до 100 раз, но обычно в 4 раза, при использовании немодифицированного интерпретатора Python. Единственный минус у Psyco, это то обстоятельство, что в настоящее время он способен генерировать машинный код только для архитектуры Intel x86.
Psyco не идет в стандартной поставке, его надо скачать и установить отдельно. Еще есть проект PyPy, который представляет собой попытку переписать PVM с целью оптимизации кода как в Psyco, проект PyPy собирается поглотить в большей мере проект Psyco.
Транслятор Shedskin C++
Фиксированные двоичные файлы (frozen binaries)
Иногда необходимо из своих программ на Python создавать самостоятельные исполняемые файлы. Это необходимо скорее для упаковки и распространения программ.
Фиксированные двоичные файлы объединяют в единый файл пакета байт-код программ, PVM и файлы поддержки, необходимые программам. В результате получается единственный исполняемый файл, например файл с расширение «.exe» для Windows.
На сегодняшний день существует три основных инструмента создания «frozen binaries»:
Вам надо загружать эти инструменты отдельно от Python, они распространяются бесплатно.
Фиксированные двоичные файлы имеют немалый размер, ибо они содержат в себе PVM, но по современным меркам из все же нельзя назвать необычно большими. Так как интерпретатор Python встроен непосредственно в фиксированные двоичные файлы, его установка не является обязательным требованием для запуска программ на принимающей стороне.
Резюме
На сегодня всё, в следующей статье расскажу о стандартных типах данные в Python, ну и в последующих статьях рассмотрим каждый тип в отдельности, а также функции и операторы для работы с этими типами.
создал файл, запустил его через пайтон, но пишет, что ошибка кодировки (файл сохранен в UTF-8) 🙁
SyntaxError: Non-ASCII character ‘\xd0’
Как работает Python?
Всем еще раз привет, сейчас расскажу о том, как работает Python, что такое интерпретатор, как работает компилятор и что такое байт-код, далее расскажу о виртуальной машине (PVM) и о производительности Python. Также о альтернативных реализациях интерпретатора.
После того, как вы установили себе Python, перейдем к теоретически-практической части и начнем с того что из себя представляет интерпретатор.
Интерпретатор
В зависимости от используемой версии Python сам интерпретатор может быть реализован как программа на языке C, как набор классов Java и в каком-либо другом виде, но об этом позже.
Запуск сценария в консоли
Давайте запустите в консоле интерпретатор:
Теперь он ожидает ввода комманд, введите туда следующую инструкцию:
ура, наша первая программа! 😀
Запуск сценария из файла
Создайте файл «test.py», с содержимым:
и выполните этот файл:
Вы увидите в консоли результат, поехали дальше!
Динамическая компиляция и байт-код
В следующий раз, когда вы запустите свою программу интерпретатор минует этап компиляции и отдаст на выполнение откомпилированный файл с расширением «.pyc». Однако, если вы изменили исходные тексты вашей программы, то снова произойдет этап компиляции в байт-код, так как Python автоматически следит за датой изменения файла с исходным кодом.
Если Python окажется не в состоянии записать файл с байт-кодом, например из-за отсутствия прав на запись на диск, то программа не пострадает, просто байт-код будет собран в памяти и при завершении программы оттуда удален.
Виртуальная машина Python (PVM)
Производительность
По этим причинам программы на Python не могут выполняться также быстро как на C/C++. Обход инструкций выполняет виртуальная система, а не микропроцессор, и чтобы выполнить байт-код, необходима дополнительная интерпретация, инструкции которой требуют большего времени, чем машинные инструкции микропроцессора.
В итоге, Python по производительности находится между традиционными компилирующими и традиционными интерпретирующими языками программирования.
Альтернативные реализации Python
То что было сказано выше о компиляторе и виртуальной машине, характерно для стандартной реализации Python, так называемой CPython (реализации на ANSI C). Однако также существует альтернативные реализации, такие как Jython и IronPython, о которых пойдет сейчас речь.
CPython
Это стандартная и оригинальная реализация Python, названа так, потому что написана на ANSI C. Именно ее мы установили, когда выбрали пакет ActivePython или установили из FreeBSD портов. Поскольку это эталонная реализация, она как правило работает быстрее, устойчивее и лучше, чем альтернативные реализации.
Jython
Цель Jython состоит в том, чтобы позволить программам на языке Python управлять Java-приложениями, точно также как CPython может управлять компонентами на языках C/C++. Эта реализация имеет беcшовную интеграцию с Java. Поскольку программный код на Python транслируется в байт-код Java, во время выполнения он ведет себя точно также, как настоящая программа на языке Java. Программы на Jython могут выступать в качестве апплетов и сервлетов, создавать графический интерфейс с использованием механизмов Java и т.д. Более того, Jython обеспечивает поддержку возможности импортировать и использовать Java-классы в программном коде Python.
Тем не менее, поскольку реализация Jython обеспечивает более низкую скорость выполнения и менее устойчива по сравнению с CPython, она представляет интерес скорее для разработчиков программ на языке Java, которым необходим язык сценариев в качестве интерфейса к Java-коду.
IronPython
Средства оптимизации скорости выполнения
Существуют и другие реализации, включая динамический компилятор Psyco и транслятор Shedskin C++, которые пытаются оптимизировать основную модель выполнения.
Динамический компилятор Psyco
Во время выполнения программы, Psyco собирает информацию о типах объектов, и затем эта информация используется для генерации высокоэффективного машинного кода, оптимизированного для объектов этого типа. После этого произведенный машинный код заменяет соответствующие участки байт-кода, тем самым увеличивается скорость выполнения.
В идеале некоторые участки программного кода под управление Psyco могут выполняться также быстро, как скомпилированный код на языке Си.
Psyco обеспечивает увеличение скорости от 2 до 100 раз, но обычно в 4 раза, при использовании немодифицированного интерпретатора Python. Единственный минус у Psyco, это то обстоятельство, что в настоящее время он способен генерировать машинный код только для архитектуры Intel x86.
Psyco не идет в стандартной поставке, его надо скачать и установить отдельно. Еще есть проект PyPy, который представляет собой попытку переписать PVM с целью оптимизации кода как в Psyco, проект PyPy собирается поглотить в большей мере проект Psyco.
Транслятор Shedskin C++
Фиксированные двоичные файлы (frozen binaries)
Иногда необходимо из своих программ на Python создавать самостоятельные исполняемые файлы. Это необходимо скорее для упаковки и распространения программ.
Фиксированные двоичные файлы объединяют в единый файл пакета байт-код программ, PVM и файлы поддержки, необходимые программам. В результате получается единственный исполняемый файл, например файл с расширение «.exe» для Windows.
На сегодняшний день существует три основных инструмента создания «frozen binaries»:
Вам надо загружать эти инструменты отдельно от Python, они распространяются бесплатно.
Фиксированные двоичные файлы имеют немалый размер, ибо они содержат в себе PVM, но по современным меркам из все же нельзя назвать необычно большими. Так как интерпретатор Python встроен непосредственно в фиксированные двоичные файлы, его установка не является обязательным требованием для запуска программ на принимающей стороне.
Резюме
На сегодня всё, в следующей статье расскажу о стандартных типах данные в Python, ну и в последующих статьях рассмотрим каждый тип в отдельности, а также функции и операторы для работы с этими типами.
создал файл, запустил его через пайтон, но пишет, что ошибка кодировки (файл сохранен в UTF-8) 🙁
SyntaxError: Non-ASCII character ‘\xd0’