Возможно ли скомпилировать Python в машинный код?

128

Насколько возможно было бы скомпилировать Python (возможно, через промежуточное представление C) в машинный код?

Предположительно, это должно быть связано с библиотекой времени выполнения Python, и любые части стандартной библиотеки Python, которые сами являются Python, также должны быть скомпилированы (и связаны).

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

Обеспечит ли это какие-либо преимущества в скорости и / или использовании памяти? Предположительно, время запуска интерпретатора Python будет исключено (хотя общие библиотеки по-прежнему нуждаются в загрузке при запуске).

Энди Валаам
источник
2
Кстати, ваш вопрос был бы ИМХО более ясным, если бы вы попросили «машинный код», а не объектный код.
Торстен Марек,

Ответы:

31

Попробуйте компилятор ShedSkin Python-to-C ++, но он далек от совершенства. Также есть Psyco - Python JIT, если нужно только ускорение. Но ИМХО это не стоит усилий. Для критических по скорости частей кода лучшим решением было бы написать их как расширения C / C ++.

слепень
источник
5
К вашему сведению, ShedSkin отказался от поддержки Windows.
sorin
2
@sorin: ну, сегодня он поддерживает windows ... code.google.com/p/shedskin/downloads/…
2
Лучшим решением с точки зрения скорости все еще может быть PyPy .
Cees Timmerman
Шедскин не работал над ним около двух лет. :(
Perkins
53

Как говорит @Greg Hewgill, есть веские причины, по которым это не всегда возможно. Однако некоторые виды кода (например, очень алгоритмический код) можно превратить в «настоящий» машинный код.

Есть несколько вариантов:

  • Используйте Psyco , который динамически генерирует машинный код. Однако вы должны тщательно выбирать, какие методы / функции преобразовывать.
  • Используйте Cython , язык, подобный Python, который скомпилирован в расширение Python C.
  • Используйте PyPy , у которого есть переводчик из RPython ( ограниченное подмножество Python, не поддерживающее некоторые из наиболее «динамических» функций Python) в C или LLVM.
    • PyPy все еще остается экспериментальным
    • не все расширения будут присутствовать

После этого вы можете использовать один из существующих пакетов (freeze, Py2exe, PyInstaller), чтобы поместить все в один двоичный файл.

В общем: общего ответа на ваш вопрос нет. Если у вас есть код Python, который критичен к производительности, постарайтесь использовать как можно больше встроенных функций (или задайте вопрос «Как сделать мой код Python быстрее»). Если это не помогает, попробуйте идентифицировать код, перенести его на C (или Cython) и использовать расширение.

Торстен Марек
источник
3
Pypy является преемником Psyco
bcattle
19

py2c ( https://github.com/pradyun/Py2C ) может конвертировать код Python в c / c ++. Я являюсь индивидуальным разработчиком py2c.

Рамчандра Апте
источник
Похоже, это полезный инструмент. Это все еще поддерживается?
Андерсон Грин
@AndersonGreen В последний раз, когда я работал над ним, он находился на ранней стадии разработки (возможно, сейчас он похож). Я ушел из проекта, потому что я ленивый. Если вы не заметили текст «Важно», теперь он перемещен на GitHub.
Ramchandra Apte
Ссылка указывает на непобедимый установщик , который, похоже, представляет собой другой проект. Py2c все еще доступен на GitHub?
Андерсон Грин
@AndersonGreen Вау, это так долго оставалось незамеченным! Вот и все.
Ramchandra Apte
Ссылка на code.google.com/p/py2c по- прежнему указывает на непобедимый установщик, поэтому его необходимо обновить сейчас.
Андерсон Грин
15

PyPy - это проект по повторной реализации Python в Python с использованием компиляции в собственный код в качестве одной из стратегий реализации (другие - это виртуальная машина с JIT, использование JVM и т. Д.). Их скомпилированные версии C в среднем работают медленнее, чем CPython, но для некоторых программ намного быстрее.

Shedskin - экспериментальный компилятор Python в C ++.

Pyrex - это язык, специально разработанный для написания модулей расширения Python. Он создан для того, чтобы преодолеть разрыв между красивым, высокоуровневым и простым в использовании миром Python и грязным низкоуровневым миром C.

PDC
источник
3
Cython - это более широко используемый и более активно развивающийся дружественный форк Pyrex.
Майк Грэм,
"красивый, высокоуровневый, простой в использовании мир Python и беспорядочный низкоуровневый мир C" - забавно, я просто подумал, насколько C и ассемблер "хороши" и просты, а Python живет в " грязный "," высокоуровневый "мир
Reversed Engineer
14

Nuitka - это компилятор Python для C ++, который связывается с libpython. Это относительно новый проект. Автор заявляет об улучшении скорости по сравнению с CPython на тесте pystone.

bcattle
источник
10

На первый взгляд это может показаться разумным, однако в Python есть много обычных вещей, которые нельзя напрямую сопоставить с представлением C без переноса значительной части поддержки среды выполнения Python. Например, на ум приходит утиная печать. Многие функции в Python, которые читают ввод, могут принимать файл или подобный файлу объект, если он поддерживает определенные операции, например. read () или readline (). Если вы подумаете о том, что нужно сделать, чтобы сопоставить этот тип поддержки с C, вы начнете представлять себе именно те вещи, которые уже выполняет система времени выполнения Python.

Существуют такие утилиты, как py2exe, которые объединяют программу Python и среду выполнения в один исполняемый файл (насколько это возможно).

Грег Хьюгилл
источник
1
Что, если бы моей целью было убедиться, что код компилируется, потому что статически скомпилированные языки (по крайней мере, на мой взгляд) с меньшей вероятностью взорвутся во время выполнения? Можно ли определить, что какое-то foo.xвыражение не будет работать, потому fooчто не будет xв момент его вызова. Есть ли какие-нибудь средства проверки статического кода для Python? Python можно скомпилировать в сборку .Net ...
Хэмиш Грубиджан
10

Pyrex - это подмножество языка Python, которое компилируется в C, созданное парнем, который первым построил понимание списков для Python. В основном он был разработан для создания оболочек, но может использоваться в более общем контексте. Cython - это более активно поддерживаемая ветвь pyrex.

ConcernedOfTunbridgeWells
источник
2
Cython - это более широко используемый и более активно развивающийся дружественный форк Pyrex.
Майк Грэм,
3

В Jython есть компилятор, ориентированный на байт-код JVM. Байт-код полностью динамичен, как и сам язык Python! Очень круто. (Да, как указывает ответ Грега Хьюгилла, байт-код действительно использует среду выполнения Jython, поэтому файл jython jar должен распространяться вместе с вашим приложением.)

Крис Джестер-Янг
источник
2

Psyco - это своего рода JIT-компилятор: динамический компилятор для Python, запускает код в 2-100 раз быстрее, но требует много памяти.

Вкратце: он запускает ваше существующее программное обеспечение Python намного быстрее, без изменений в вашем исходном коде, но он не компилируется в объектный код так, как это сделал бы компилятор C.

Пьер-Жан Кудер
источник
2

Ответ: «Да, это возможно». Вы можете взять код Python и попытаться скомпилировать его в эквивалентный код C, используя CPython API. На самом деле, раньше был проект Python2C, который делал именно это, но я не слышал о нем много лет (еще во времена Python 1.5 я видел его в последний раз).

Вы можете попытаться перевести код Python на собственный язык C, насколько это возможно, и вернуться к API CPython, когда вам понадобятся реальные функции Python. Я сам обдумывал эту идею последние месяц или два. Однако это ужасно много работы, и огромное количество функций Python очень сложно перевести на C: вложенные функции, генераторы, что угодно, кроме простых классов с простыми методами, все, что связано с изменением глобальных объектов модуля извне модуля и т. Д. , и т.д.

Томас Воутерс
источник
2

Это не компилирует Python в машинный код. Но позволяет создать общую библиотеку для вызова кода Python.

Если то, что вы ищете, - это простой способ запустить код Python из C, не полагаясь на материал execp. Вы можете создать общую библиотеку из кода Python, обернутого несколькими вызовами API встраивания Python . Приложение представляет собой разделяемую библиотеку, поэтому вы можете использовать ее во многих других библиотеках / приложениях.

Вот простой пример создания общей библиотеки, которую вы можете связать с программой C. Общая библиотека выполняет код Python.

Будет выполнен файл python pythoncalledfromc.py:

# -*- encoding:utf-8 -*-
# this file must be named "pythoncalledfrom.py"

def main(string):  # args must a string
    print "python is called from c"
    print "string sent by «c» code is:"
    print string
    print "end of «c» code input"
    return 0xc0c4  # return something

Вы можете попробовать python2 -c "import pythoncalledfromc; pythoncalledfromc.main('HELLO'). Он выведет:

python is called from c
string sent by «c» code is:
HELLO
end of «c» code input

Общая библиотека будет определяться следующим образом callpython.h:

#ifndef CALL_PYTHON
#define CALL_PYTHON

void callpython_init(void);
int callpython(char ** arguments);
void callpython_finalize(void);

#endif

Связано callpython.cэто:

// gcc `python2.7-config --ldflags` `python2.7-config --cflags` callpython.c -lpython2.7 -shared -fPIC -o callpython.so

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <python2.7/Python.h>

#include "callpython.h"

#define PYTHON_EXEC_STRING_LENGTH 52
#define PYTHON_EXEC_STRING "import pythoncalledfromc; pythoncalledfromc.main(\"%s\")"


void callpython_init(void) {
     Py_Initialize();
}

int callpython(char ** arguments) {
  int arguments_string_size = (int) strlen(*arguments);
  char * python_script_to_execute = malloc(arguments_string_size + PYTHON_EXEC_STRING_LENGTH);
  PyObject *__main__, *locals;
  PyObject * result = NULL;

  if (python_script_to_execute == NULL)
    return -1;

  __main__ = PyImport_AddModule("__main__");
  if (__main__ == NULL)
    return -1;

  locals = PyModule_GetDict(__main__);

  sprintf(python_script_to_execute, PYTHON_EXEC_STRING, *arguments);
  result = PyRun_String(python_script_to_execute, Py_file_input, locals, locals);
  if(result == NULL)
    return -1;
  return 0;
}

void callpython_finalize(void) {
  Py_Finalize();
}

Вы можете скомпилировать его с помощью следующей команды:

gcc `python2.7-config --ldflags` `python2.7-config --cflags` callpython.c -lpython2.7 -shared -fPIC -o callpython.so

Создайте файл с именем, callpythonfromc.cкоторый содержит следующее:

#include "callpython.h"

int main(void) {
  char * example = "HELLO";
  callpython_init();
  callpython(&example);
  callpython_finalize();
  return 0;
}

Скомпилируйте и запустите:

gcc callpythonfromc.c callpython.so -o callpythonfromc
PYTHONPATH=`pwd` LD_LIBRARY_PATH=`pwd` ./callpythonfromc

Это очень простой пример. Это может работать, но в зависимости от библиотеки может быть все еще сложно сериализовать структуры данных C в Python и с Python на C. Все можно несколько автоматизировать ...

Нуитка может быть полезна.

Также есть нумба, но они оба не стремятся делать именно то, что вы хотите. Создание заголовка C из кода Python возможно, но только если вы укажете, как преобразовать типы Python в типы C, или можете вывести эту информацию. См. Python astroid для анализа Python ast.

amirouche
источник