Топ-7 бесплатных компиляторов и интерпретаторов Python
В этой статье мы поговорим о компиляторах и интерпретаторах Python и обсудим 7 самых популярных из них: CPython, Jython, IronPython, ActivePython, Nuitka, PyJS и Stackless Python.
Итак, давайте начнем.
Компиляторы и интерпретаторы Python
Возможно, вы слышали, что Python называется интерпретируемым языком программирования. Это означает, что компилятор преобразует исходный код (.py-файл) в байт-код (.pyc-файл), который затем исполняется виртуальной машиной Python (PVM).
А теперь рассмотрим наш список компиляторов и интерпретаторов Python:
1. CPython
Это наиболее распространённая, эталонная реализация Python. CPython может быть определен как интерпретатор и компилятор, поскольку он компилирует код Python в байт-код перед его интерпретацией.
- Разработчики: разработчики Python и сообщество Python, при поддержке Python Software Foundation
- Последняя версия: 3.8.2 (24 февраля 2020)
- Написан на: C
- Тип: интерпретатор Python
Особенностью CPython является то, что он использует глобальную блокировку интерпретатора (GIL) для каждого процесса интерпретатора CPython. Это означает, что в рамках одного процесса в любой момент времени только один поток может обрабатывать байт-код Python. Параллельное выполнение кода может быть достигнуто только с помощью отдельных процессов интерпретатора CPython, которыми управляет многозадачная ОС. В Python есть модуль multiprocessing, который упрощает создание процессов.
2. Jython
Jython является преемником JPython. Это реализация Python для виртуальной машины Java. Jython одновременно и компилятор, и интерпретатор.
- Первый выпуск: 17 января 2001
- Последняя версия: 2.7.2 (21 марта 2020)
- Написан на: Python и Java
- Тип: интерпретатор Python
Jython компилирует исходный код Python в байт-код Java. Это означает, что мы можем запустить его на любой машине, на которой работает JVM (виртуальная машина Java). Программы, выполняемые в среде Jython, могут одновременно использовать классы языков Java и Python.
3. IronPython
Это реализация Python, предназначенная для платформы Microsoft .NET или Mono.
- Автор: Джим Хугунин
- Первый выпуск: 5 сентября 2006
- Последняя версия: 2.7.9 (Октябрь 9, 2018)
- Написан на: C#
- Тип: интерпретатор Python
IronPython поддерживает динамическую компиляцию и интерактивную консоль. Скрипты Python могут взаимодействовать с объектами .NET.
4. ActivePython

ActivePython — это дистрибутив интерпретатора языка программирования Python от компании ActiveState. Распространяется в версиях для Microsoft Windows, Windows NT, Mac OS X, Linux, Solaris, AIX и HP-UX. ActivePython включает более 300 популярных пакетов Python, прошедших предварительную проверку на наличие устаревших или уязвимых версий, несовместимости и неправильного лицензирования.
5. Nuitka
Nuitka — это транспайлер, который транслирует код Python в исполняемые файлы или исходный код C/C++.
- Разработчики: Кей Хайен, сообщество
- Последняя версия: 0.6.5 (30 июля 2019)
- Тип: транспайлер
Nuitka работает с различными версиями Python и позволяет создавать автономные приложения, даже когда Python не установлен на целевом компьютере.
6. PyJS
PyJS — это фреймворк для для разработки веб-приложений. Он позволит вам использовать Python для разработки как клиентской части веб-приложений, так и десктоп-приложений.
- Автор: Джеймс Таубер
- Первый выпуск: март 2007
- Последняя версия: 0.8.1a (6 мая 2012)
- Написан на: Python, JavaScript
Раньше PyJS назывался Pyjamas. Он транслирует ваш код Python в JavaScript и позволяет запускать его в браузере. PyJS поставляется с AJAX-фреймворком и библиотекой виджетов.
7. Stackless Python
Stackless Python — это интерпретатор Python. Он назван так из-за отказа от использования стандартного стека вызовов языка C в пользу собственного стека.
- Автор: Кристиан Тисмер
- Первый выпуск: 1998
- Последняя версия: 3.6.6 (12 сентября 2018)
- Написан на: C, Python
- Тип: интерпретатор Python
Особенностью Stackless являются микропотоки (microthreads), которые позволяют избежать чрезмерного расхода системных ресурсов, присущего стандартным потокам операционной системы. В дополнение к стандартным возможностям Python, Stackless добавляет поддержку сопрограмм, коммуникационных каналов и сериализации задач.
Итак, мы рассмотрели некоторые из самых популярных компиляторов и интерпретаторов Python. И можем заключить, что Python не ограничивается только CPython, основной реализацией. Также существуют другие реализации, среди которых можно выбрать ту, которая больше всего подходит для решения поставленных задач.
Триафлай — Технологии среды разработки
Скорость и стабильность системы обеспечиваются специальными компонентами Python.

Python — высокоуровневый язык программирования общего назначения, ориентированный на повышение производительности разработчика и читаемости кода. Это активно развивающийся кроссплатформенный язык программирования, с полностью открытым исходным кодом.
PyPy — интерпретатор языка программирования Python. Он также призван обеспечить совместимость, гибкость и быстроту реализации языка программирования Python и позволяет реализовывать новые возможности без необходимости программирования на языке низкого уровня. Позволяет организовать работу In-memory в базе данных.
Pandas — программная библиотека на языке Python для обработки и анализа данных. Предоставляет специальные структуры данных и операции для манипулирования числовыми таблицами и временными рядами. Обеспечивает поддержку высокопроизводительных вычислений.
NumPy — библиотека с открытым исходным кодом для языка программирования Python. Обеспечивает поддержку многомерных массивов и высокоуровневых математических функций, предназначенных для работы с многомерными массивами.
Celery — это технология организации асинхронной очереди задач с открытым исходным кодом или очередь заданий, основанная на распределенной передаче сообщений. Реализована в работе рассылки почты, построения отчетов, загрузки данных и прочих системных процессов.
PostgreSQL — свободная объектно-реляционная система управления базами данных (СУБД), имеющая традиционные возможности коммерческих СУБД с расширениями, которые есть в СУБД нового поколения. PostgreSQL – это свободное и полностью открытое программное обеспечение.
D3.js — это библиотека JavaScript для создания динамических интерактивных визуализаций данных в веб-браузерах. Она отвечает за визуализацию графических представлений в системе.
Docker — программное обеспечение для автоматизации развёртывания и управления приложениями в средах с поддержкой контейнеризации. Отвечает за виртуализацию и изоляцию модулей системы.
RabbitMQ — программный брокер сообщений на основе стандарта AMQP — тиражируемое связующее программное обеспечение, ориентированное на обработку сообщений. Поддерживает горизонтальное масштабирование для построения кластерных решений.
Возможности интеграции с другими системами
Из коробки поддерживается подключение к различным внешним источникам как через готовые коннекторы к 1С, SQL, REST, SOAP, XLS и пр., так и по ODBC с возможностью гибкой кастомизации данных на входе. В рамках проекта могут быть реализованы интеграции с mpp-системами и платформами управления большими данными.

Подготовка данных: для загрузки данных используются коннекторы. При загрузке данных возможны произвольные трансформации, задаваемые на Python в изолированной «песочнице».
Хранение и загрузка данных: система позволяет подключаться к внешним данным двумя способами: импорт данных в хранилище позволяющее производить обработку данных и подключение онлайн (для ODBC-источников) без возможности трансформации данных, исключительно для построения отчетов и визуализаций. Для вычислений и построения отчетов используется технология in-memory базы данных собственной разработки MemDB.
Структура данных
- Веб-сервер. Apache или Nginx
- Воркеры, обрабатывающие запросы сервера. Python, Django и uWSGI
- Supervisord, контролирует работу сервисов, демонизирует их
- Celery — Компонент, обрабатывающий очередь сообщений
- Кэш метаданных на уровне каждого воркера
- Кэш посчитанных отчетов
- MemDB — In-Memory база данных. Хранит в оптимизированном виде часть базы с данными, запущена под PyPy
- RabbitMQ — Очередь сообщений
- Обработчики сообщений очереди. Celery и Pyton использует Pandas для вычислений отчетов
- NetDB — База данных постоянного хранения на PostgreSQL
- Загрузчики данных
- Вспомогательная база данных для ETL на PostgreSQL
- Набор источников данных — файлы, системы и сервисы
Как установить лицензионную защиту кода на Python и обезопасить данные с помощью HASP?

Всем привет, я Вячеслав Жуйко – Lead команды разработки Audiogram в MTS AI.
При переходе от On-Cloud размещений ПО на On-Premises в большинстве случае перед вами неизбежно встанет задача защиты интеллектуальной собственности – и она особенно критична для рынка AI, где задействуются модели, обладающие высокой ценностью для компании. К тому же, в этой сфере широко используется интерпретируемый язык Python, ПО на котором содержит алгоритмы, являющиеся интеллектуальной собственностью компании, но фактически распространяется в виде исходных кодов. Это не является проблемой для On-Cloud решений, но в случае с On-Premises требует особой защиты как от утечек кода, так и самих данных.
Рассказываю реальную историю решения этой, казалось бы, не самой тривиальной задачи.
Почему нам потребовалось шифровать код и данные
Мы с коллегами разрабатываем Audiogram — платформу синтеза и распознавания речи. Она состоит из большого количества микросервисов, связанных между собой. Обычно мы разворачивали это решение On-Cloud, и поэтому задачи защитить код у нас не возникало. Однако все изменилось, после того как к нам пришел заказчик, которому нужно было установить Audiogram On-Premises. Мы не могли передать код программы клиенту — это создало бы опасность кражи нашей интеллектуальной собственности. Именно поэтому мы начали искать способ зашифровать информацию и остановились на одном простом и эффективном варианте. Далее я расскажу подробнее, как сгенерировать и зашифровать код. Итак, разберем все по шагам.
Генерируем из кода на Python код на C++
Для простоты представим, что у нас уже есть минимальный проект на Python, который загружает файл с данными, а затем использует их. Пусть это будет совсем просто:
with open(path, 'rb') as f: data = f.read() use_data(data)
Понятно, что мы не можем передавать заказчику ни исходный код, ни тем более данные. Последние предстоит зашифровать, а код обфусцировать.
В случае с кодом я использовал Cython – для генерации из кода на Python кода на C или C++. Вообще способы генерации могут быть разными, а самым распространенным является Setuptools. Однако сходу у меня не вышло написать setup.py для генерации исполняемого файла (не библиотеки), поэтому пошел путем использования Cython через коммандную строку.
Примеры вызовов Cython:
cython -3 --no-docstrings --fast-fail --output-file lib.c lib.py cython -3 --no-docstrings --fast-fail --cplus --output-file lib.cpp lib.py cython -3 --no-docstrings --fast-fail --embed --output-file app.c app.py
Рассмотрим используемые параметры:
-3 — версия Python
—no-docstrings — не включает Python Docstrings в сгенерированный файл
—fast-fail — процесс генерации прерывается по первой ошибке
—embed — включает функцию main() , что позволяет собрать как исполняемый файл и запускать не через интерпретатор, а напрямую: ./app
—cplus — генерирует C++ вместо C
Теперь полученные C или C++ файлы нужно собрать. Безусловно, применить можно разные подходы, в том числе написать Makefile. Я пошел схожим путем, как и в случае с Cython, и вызвал сборку из командной строки. Если попробуете повторить, сначала убедитесь, что у вас установлены пакеты build-essential python-dev-is-python3 .
Примеры вызова сборки из коммандной строки:
gcc $(python3.8-config --cflags) -fPIC -g0 -s -shared lib.c -o lib.so gcc $(python3.8-config --cflags) -fPIC -g0 -s app.c -o app $(python3.8-config --libs --embed)
Рассмотрим используемые параметры:
python3.8-config —cflags — возвращает CFLAGS
-fPIC — генерировать позиционно независимый код
-g0 — не включать информацию для отладки
-s — удаляет таблицу символов и информацию о релокации
-shared — собирает динамическую библиотеку
python3.8-config —libs —embed — возвращает строку с библиотеками для линкера
Параметры -g0 и -s я добавил для затруднения отладки.
Написал скрипт на Python, который проходится по дереву исходников и выполняет две вышеописанные операции: ситонизирует и собирает, после чего удаляет исходник.
Итак, с кодом разобрались – мы больше не поставляем исходники, заменив их на бинарные ELF файлы без Python Docstrings и отладочной информации. Кстати, приятный бонус – ситонизация может увеличить скорость работы по сравнению с Python, особенно если используется type hinting.
Все ли всегда так гладко? Увы, нет. Cython отстает по фичам от Python, например, не поддерживаются «the walrus operator», Data Classes, а функции из inspect возвращают неадекватные результаты – это только то, с чем столкнулись мы. Это все неприятно, но жить с этим можно. К тому же, где-то с проблемой можно справиться, например, в случае с Data Classes достаточно добавить annotations вручную, после чего их можно использовать.
Шифруем данные
Теперь нам осталось зашифровать данные. Для шифрования используем SDK от одного из решений для HASP, в нашем случае это Sentinel.
Средства шифрования файлов в SDK предоставляются «из коробки». Данные зашифрованы, теперь предстоит научить наш код на Python их расшифровывать. Сделать это можно, просто добавить вызов:
with open(path, 'rb') as f: data = f.read() data = decrypt(data) use_data(data)
Правда, сосчитав количество вариантов загрузки данных из файла в реальном коде, я решил все же пойти другим путем. В самых общих словах – где-то данные загружаются как в приведенном примере, где-то построчно, а самое страшное: f передается как параметр в сторонний пакет, в который не хотелось бы погружаться вообще.
В итоге, показалось проще сделать на основе Sentinel SDK статическую библиотеку на C++ libsentinel.a с единственной экспортируемой функцией:
std::vector sentinel_decrypt(const std::string& path);
Почему именно статическую? Если в файловой системе будет лежать библиотека libsentinel.so с экспортируемой функцией sentinel_decrypt(), воспользоваться ей сможет любой.
Написал на Cython подмену стандартного механизма чтения файла:
import os import io from typing import Union, List, AnyStr, Iterator from libcpp.vector cimport vector from libcpp.string cimport string cdef extern from "sentinel.h": vector[unsigned char] sentinel_decrypt(const string& path) except + def sentinel_open(path: Union[str, bytes, os.PathLike], mode: str, **kwargs) -> 'SentinelFileIo': return SentinelFileIo(path, mode, **kwargs) class SentinelFileIo: def init(self, path: Union[str, bytes, os.PathLike], mode: str, **kwargs) -> None: if isinstance(path, os.PathLike): path = str(path) if isinstance(path, str): path = path.encode('utf-8') data = bytes(sentinel_decrypt(path)) encoding = kwargs.get('encoding', None) if encoding is not None: data = data.decode(encoding) self._data = data self._size = len(self._data) self._pos = 0 def close(self) -> None: self._data = None def closed(self) -> bool: return self._data is None def tell(self) -> int: self._ensure_open() return self._pos def seek(self, offset: int, whence: int = io.SEEK_SET) -> int: self._ensure_open() if whence == io.SEEK_SET: if offset < 0: raise ValueError(f'negative seek position ') self._pos = offset else: raise io.UnsupportedOperation("can't do nonzero cur-relative seeks") return self._pos def read(self, n: int = -1) -> AnyStr: self._ensure_open() data = self._data[self._pos:self._pos+n] if n >= 0 else self._data[self._pos:] self._pos += len(data) return data def readline(self, limit: int = -1) -> AnyStr: self._ensure_open() data = self._data[self._pos:] stream = io.BytesIO(data) if isinstance(data, bytes) else io.StringIO(data) line = stream.readline(limit) self._pos += len(line) return line def readlines(self, hint: int = -1) -> List[AnyStr]: self._ensure_open() data = self._data[self._pos:] stream = io.BytesIO(data) if isinstance(data, bytes) else io.StringIO(data) lines = stream.readlines(hint) self._pos += sum([len(line) for line in lines]) return lines def __enter__(self) -> 'SentinelFileIo': return self def __exit__(self, exc_type, exc_val, exc_tb) -> bool: self.close() return exc_type is None def __iter__(self) -> Iterator[AnyStr]: self._ensure_open() while self._pos < self._size: yield self.readline() def _ensure_open(self) ->None: if self.closed(): raise ValueError('I/O operation on closed file.')
Все, что теперь остается сделать с кодом — это поменять open() на sentinel_open() больше не трогая ни строчки. Теперь код выглядит так:
with sentinel_open(path, 'rb') as f: data = f.read() use_data(data)
На самом деле действий нужно чуть больше:
- переименовать файл .py -> .pyx
- вставить вышеприведенный кусок кода в файл
Теперь с кодом точно все. Осталось сгенерировать C++ код из – теперь уже – Cython кода вышеописанным способом, а также собрать, прилинковав libsentinel.a и библиотеку из Sentinel SDK.
Примеры вызовов:
g++ $(python3.8-config --cflags) -fPIC -g0 -s -shared lib.cpp -o lib.so -lsentinel -lhasp_cpp_linux_x86_64 g++ $(python3.8-config --cflags) -fPIC -g0 -s app.cpp -o app $(python3.8-config --libs --embed) -lsentinel -lhasp_cpp_linux_x86_64
Теперь у нас есть зашифрованный файл данных и бинарный ELF-файл, который сможет расшифровать их при загрузке. Результат достигнут? Казалось бы, да, но есть еще особенность.
В собранном файле, к которому был прилинкован libsentinel.a, можно найти строку с vendor code, имея который и Sentinel SDK, данные возможно расшифровать. На помощь нам приходит утилита из того же Sentinel SDK: Envelope, которая особым образом трансформирует файл, после чего вызов утилиты strings больше не выводит ни единой читаемой строки.
Пример вызова утилиты:
Sentinel-LDK/VendorTools/Envelope/linuxenv --vcf:company-product.hvc --fid:1 input-file.so output-file.so
А как же лицензионная защита упомянутая в заголовке статьи? После обработки утилитой Envelope, ELF-файл может быть использован только на машине с установленной лицензией. А это значит, что код под надежной защитой Вот такой способ мы нашли при установке клиенту On-Premises-версии Audiogram. Пишите в комментариях, была ли полезна вам моя статья, и делитесь лайфхаками, как вы решаете задачу с защитой кода и данных.
Решение от Sentinel – это коммерческий продукт и нужна платная лицензия. И Sentinel ушел из России, но есть другие похожие решения. Например, Guardant.
Язык программирования Python. Часть 2. Компиляторы и Интерпретаторы
При взаимодействии человека и компьютера мостиком выступает высокоуровневый язык программирования. Высокоуровневые языки программирования создаются такими, чтобы писать на их было удобно человеку, компьютер такие языки не понимает. Компьютер — вычислительная машина, для которой понятны только наборы из нулей и единиц.
Так как же превратить наш исходный код во что-то, что могут прочитать компьютеры? Совсем, как в реальной жизни, для перевода с одного языка на другой обратиться к переводчикам. В программировании их называют трансляторами – это компилятор и интерпретатор. Оба делают перевод исходного кода в машинный, но делают это по-разному.
Компилятором называется транслятор, который получает файл с кодом, расшифровывает его и переводит в машинные коды целиком. Получается исполняемый файл, который можно запустить внутри той или иной операционной системы (например, исполняемый файл .exe для запуска в MS Windows). В результате работы компилятора пользователь может пользоваться программой, но не может прочитать ее исходный код.
Первый готовый к работе Компилятор был представлен в 1952 году Грейс Хоппер — американским учёным и коммодором флота США.
Интерпретатор – это транслятор, который запускает код построчно. Интерпретатор получает команду, написанную на языке программирования, читает ее и сразу же выполняет. Условно его работа может выполняться в интерактивном или пакетном режимах.
Интерактивный режим еще называют циклом «чтения – исполнения – печати» (Read-eval-print loop или REPL). Пользователь пишет в консоли какую-то команду и как только нажимает клавишу Enter, команда выполняется. Такой режим используют для быстрых подсчетов, отправки запросов и других действий, которым достаточно одной команды.
Пакетный режим используется для более крупных задач, которые неудобно писать построчно и выполнять сразу же. Пользователь пишет код в файле и сохраняет его с нужным расширением. Интерпретатор получает файл, построчно считывает написанный там код и выполняет его.
Первый Интерпретатор был создан в 1958 году Стивом Расселом — американским ученым-программистом, наиболее известным благодаря созданию «Spacewar!», одной из самых ранних видеоигр.
Интерпретатор можно сравнить с синхронным переводчиком, который сразу же озвучивает перевод. А компилятор — с литературным переводчиком, который переводит тексты, а потом отправляет перевод тем, кто будет с ним работать.
Компилятор и интерпретатор делают одну и ту же работу и у вас может возникнуть логичный вопрос — а что же лучше? Процесс компиляции или интерпретации? Тут нет очевидного ответа, ведь если бы он был, то один из этих трансляторов давно бы перестал существовать. У каждого из них есть свои преимущества и недостатки.

Интерпретация подходит для распространения небольших программ. При этом запускать интерпретируемую программу можно на любом процессоре или платформе – MacOS или Windows, например. Интерпретатор берет всю работу по совместимости на себя, для работы кода важно одно — чтобы у всех стоял интерпретатор.

Для крупных проектов выбирают компиляцию: она быстрее и безопаснее. Однако в этом случае придется позаботиться о совместимости, хотя мы уже привыкли устанавливать только подходящий нашему «железу» софт.

Компиляция работает быстрее интерпретации, потому что компилятор запускается только один раз: делает перевод и отдает его процессору. Интерпретатор же запускается на каждой строке кода и нужен при каждом запуске программы. Кроме того, программный код в компиляторе уже переведен в машинный, поэтому требуется намного меньше времени для его исполнения.

Для интерпретаторов скорость является самым большим недостатком. Для каждой программы выполняется достаточно много переводов, фильтраций, что приводит к замедлению работы и мешает выполнению программного кода.

С помощью интерпретатора проще добавить дополнительные функции, не расширяя язык программирования. Также их проще переписать или перекомпилировать для новых платформ.
Написание же компилятора для процессора требует добавления множества функций, или полной переработки. Но как только компилятор написан, можно скомпилировать много интерпретаторов и на выходе получить перспективный язык.
Что все это значит для языка программирования Python?
Python является интерпретируемым высокоуровневым языком программирования. Это означает, что он наследует все описанные выше преимущества и недостатки процесса интерпретации. Отметим, что это вовсе не делает Python, как и любой другой язык программирования, «хорошим» или «плохим». Каждый язык индивидуален и добавляет свои уникальные особенности к обоим наборам.
А о том, каков именно язык программирования Python, мы расскажем в следующей статье!
Следите за продолжением…

Наш адрес: г. Могилев, ул. Чайковского, дом 11 (БЦ «БРИКС»), офис 214
