Здравствуй, читатель.
Я люблю Python таким, какой он есть: шустрый, лаконичный, имеет из-под коробки очень много полезных вещей (например, можно узнать, является ли число полиморфом всего в 1 строку).
На нем в данный момент пишу клеточный автомат — упрощенное воссоздание жизни в чашке Пельтье. 5 классов живых существ, наследственность, влияние «силы водорода» на жизнь и развитие клетки. Но это уже другая история.
Собственно, это не осталось незамеченным со стороны окружающих. Появились люди, которые хотели «погонять» за свою колонию цианобактерий и вывести их в элиту своей чашки, вытеснив других. Но тут проявляется, по мнению некоторых, не очень хорошая особенность данного ЯП — он (почти) не компилируем — приходится с собой таскать 80 мегабайт дополнительного груза. А что если…
Цель — сделать Python более добрым по отношению к простому смертному пользователю, то есть:
— не принуждаем юзера ставить среду;
— не заставляем качать большие файлы (> 25 мб);
— не посылаем танцевать с бубнами, «нажми там, затем нарисуй пятиконечную звезду»;
— не теряем скорость выполнения приложения. Лишь чуток подождать в начале запуска.
Для своих целей я избрал вот такую алхимию:
1) Берем девственно чистый Python (в статье используется Python 3.4, но вас ничто не сдерживает использовать более поздниеранние версии), без Tkinter, IDLE, pydoc, тестов;
2) Добавляем в него дополнительные (не стандартные, + нами специально изъятые) нужные модули, использующиеся в приложении, добавляем их в "/lib/" директорию. Помещаем в отдельную директорию папку с интерпретатором + исполняемый скрипт;
3) Сжимаем;
4) Создаем на компилируемом ЯП (самое простое — pascal) маленькую программку, которая расшакалит распакует это дело уже на компьютере конечного пользователя и запустит;
5) Задача, выполнена, my master!
Пункты 1 и 2. Чистим Python
Этот пункт можно проделать и самостоятельно. Выполняем их для уменьшения размера готового велосипеда. (В развернутом состоянии интерпретатор у меня весит 60,9 мб, в сжатом — всего 11,9 мб, то есть сжатие составляет 80,5%, что немаловажно).
Ссылка на Dropbox. (12 мб)
Затем складываем zip архив, файл (.py или .pyc) и сопутствующие файлы (картинки, ресурсы и проч.) в одну папку. Важно! Основной файл python должен называться main.py или main.pyc во избежание путаницы с другими .py или .pyc файлами вашего проекта.
Получится что-то похожее:
dir_test/
… python.zip
… main.py
… image.bmp
… README.txt
Пункт 3
Теперь сжимаем всё содержимое папки (!!!) в zip файл. Зачем мы снова архивируем интерпретатор? Интерпретатор мы сжимаем с высокой степенью сжатия и, если мы при каждой пробе будем заново архивировать его, то будет уходить довольно много времени.
Пункт 4
Теперь остается написать программу на pascal/delphi:
program python;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Zip, ShellAPI;
var a, path : string;
zip : TZipFile;
i, s, p : integer;
begin
try
// если уже проводилась распаковка...
if fileexists('.sourse\main.py') or fileexists('.sourse\main.pyc') then begin
writeln('Starting...');
if fileexists('.sourse\main.py') then ShellExecute(0, nil, PChar('"'+ExtractFilePath(ParamStr(0))+'\.sourse\python\py.exe"'), PChar(string('"'+ExtractFilePath(ParamStr(0))+'\.sourse\main.py"')), '', 1)
else if fileexists('.sourse\main.pyc') then ShellExecute(0, nil, PChar('"'+ExtractFilePath(ParamStr(0))+'\.sourse\python\py.exe"'), PChar(string('"'+ExtractFilePath(ParamStr(0))+'\.sourse\main.pyc"')), '', 1)
else begin
write('File not found! Press any key...');
read(i);
end;
exit();
end;
//распаковываем zip-файл
zip := TZipFile.Create;
zip.UTF8Support := True;
s := 0; // индикатор наличия main файла
p := 0; //индикатор наличия python
zip.Open('sourse.zip', zmRead);
for I := 0 to zip.FileCount - 1 do
begin
writeln(zip.FileNames[i], ' extracting...');
if zip.FileNames[i] = 'main.py' then s := 1;
if zip.FileNames[i] = 'main.pyc' then s := 2;
if zip.FileNames[i] = 'python.zip' then p := 1;
zip.Extract(zip.FileNames[i], '.sourse');
end;
zip.Close;
DeleteFile('sourse.zip');
//начинаем работу с распаковкой пайтона
write('Extracting Python, it may take a few seconds...');
zip.Open('.sourse\python.zip', zmRead);
zip.ExtractAll('.sourse\python');
write('Done!'+#10#13 +'Starting...');
zip.Destroy();
DeleteFile('.sourse\python.zip');
//проверки
if s = 0 then //если в ходе распаковки основной файл не был обнаружен
begin
write('No .py or .pyc file!', #10#13, 'Press any key...');
read(a);
exit();
end;
if p = 0 then //если в ходе распаковки python не был обнаружен
begin
write('No Python!', #10#13, 'Press any key...');
read(a);
exit();
end;
//сам запуск
if s = 1 then ShellExecute(0, nil, PChar('"'+ExtractFilePath(ParamStr(0))+'\.sourse\python\py.exe"'), PChar(string('"'+ExtractFilePath(ParamStr(0))+'\.sourse\main.py"')), '', 1) //PChar(string(ExtractFilePath(ParamStr(0))+'\.sourse\main.py'))
else ShellExecute(0, nil, PChar('"'+ExtractFilePath(ParamStr(0))+'\.sourse\python\py.exe"'), PChar(string('"'+ExtractFilePath(ParamStr(0))+'\.sourse\main.pyc"')), '', 1); //PChar(string(ExtractFilePath(ParamStr(0))+'\.sourse\main.py'))
exit();
except
//на случай, если что случится
on E: Exception do begin
Writeln(E.ClassName, ': ', E.Message);
write('Broken! Press any button...');
read(a);
end;
end;
end.
Программа весит 559 кб, что очень недурно.
Для проверки напишем скрипт-пробник на python:
print('Привет! Я - переносной скрипт python и я занимаю в запакованном виде менее 20 мб!')
print('Сейчас я попытаюсь импортировать модуль random...')
try:
import random
print('Хех, мне это удалось!')
print('Держи псевдослучайное число :', random.randrange(0,100))
except:
print('%( Не получилось')
ragnarok = input('Нажмите любую кнопку...')
Всё это весит в запакованном виде 12 мегабайт (но по увеличению количества кода вес не будет существенно меняться) и работает быстро — по тестам первая распаковка длится не более 4 секунд, а последующие запуски — менее полсекунды.
В развернутом виде мы получаем 60,9 мегабайта.
Ссылка на тест:
https://www.dropbox.com/s/17xdpeju7u0oieh/python%20test.zip?dl=0 (Dropbox, 12 мб)
Подведем итог
Мы способны полноценно запускать python приложения на компьютерах под управлением Windows, где не стоит интерпретатор Python (также в ситуациях с установленной 2.x версией), увеличили мобильность приложения, не теряя скорости выполнения.
Спасибо, что дочитали мою статью. У меня мало опыта в таком деле, но я буду стараться! Следующей будет статья про клеточный автомат.
Автор: Koiki