LoginSignup
342
398

More than 1 year has passed since last update.

超軽量、超高速な配布用Python「embeddable python」

Last updated at Posted at 2018-03-22

0.wybf0c7nin9.png

追記:18-10-31 パッケージのインストールエラーに対応する方法を追記
追記:20-11-18 エラー対応に追記

Pythonを独立したExeファイルとして書き出すPyInstallerの記事を以前書きました
しかし、この方法よりも実行がより高速、より軽量な方法がありました。
embeddable pythonと呼ばれるもので、Windows限定ではありますが、ダウンロードしてきた圧縮状態なら 7 MB解凍しても 14 MB程度のPython実行環境です。
もちろんこのままではパッケージを使えませんので、そのパッケージ導入方法も合わせて記載します。

公式
3. Windows で Python を使う — Python 3.6.4 ドキュメント

ダウンロード

Python Releases for Windows | Python.org

上記ページにあるWindows x86-64 embeddable zip fileをダウンロードして解凍。
もしくは、WindowsPowerShellで以下を実行。(wgetが使えるため楽だから)

PowerShell
# 作業用フォルダへ移動(適当なところでOK)
cd (適当なところ)
# PowerShellのwgetでファイルをダウンロード(ウィルス対策ソフトに注意)
wget "https://www.python.org/ftp/python/3.6.4/python-3.6.4-embed-amd64.zip" -O "epython_zip.zip"
# 解凍
Expand-Archive -Path epython_zip.zip -DestinationPath epython

0.igcn5ygx4xs.png

最小構成

pythonNN.dll、pythonNN.zip、vcruntime140.dllがあればPythonは動作します。
python.exeは、pythonNN.dllへ引数を送るだけの実行ファイルですので、独自C++に組み込むときは必要ありません。
(pythonNN.zipは圧縮ファイルですが、このままで機能しますので解凍してはいけません。 フォルダ名さえ気をつけていれば解凍しても使えます。詳しくは下部の「エラー対応」項目を参照)

今回は他の構成ファイルを使うのでそのままにしておいてください。

0.9x3kncj8xjq.PNG

PIP

pythonNN._pthファイルを修正。

そのままではget-pip.pyが使えないので、少し手直しをする必要があります。
ダウンロードしてきたファイルの中にあるpython36._pth(pythonのバージョンによって数字部分が変わる)というファイルをエディタで開いて、

python36._pth
# import site

を、

python36._pth
import site

とコメント解除する必要があります。
python 3.6 embed cannot get pip · Issue #7 · pypa/get-pip · GitHub

※ pythonのバージョンによっては、以下の1行もpython36._pthに追記します。(コメントの@alphya様からの情報)

./Lib/site-packages

それから、https://bootstrap.pypa.io/get-pip.pyのファイルをダウンロードするか、ダウンロード処理をPowerShellで以下の通りに実行。

PowerShell
# 解凍したepythonの中身へ移動
cd epython
# そのままではwgetでダウンロードできないので、下記を実行(PowerShellではデフォルトでTLS1.2が非対応のため)
# 一時的にTLS1.2を有効にします(PowerShellを閉じると解除される)
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12;
# Wget
wget "https://bootstrap.pypa.io/get-pip.py" -O "get-pip.py"

pthファイル

上記記事にあるように、3.6系以上のバージョンの場合は、ダウンロードしてきたpython.exeと同じディレクトリに適当なファイル名に.pth拡張子をつけたファイルを作成し、以下の内容を記載して保存します。

import sys; sys.path.append('')

コマンドプロンプトで作業

これでget-pip.pyが使えるのでインストール作業をしますが、
ここからはコマンドプロンプトで作業します。(PowerShellではモジュールエラーで実行出来なかった。)

0.0v0n1hrp4rv.PNG

要注意点

get-pipに限らず、ルート環境にpythonがインストールされているときはembeddable pythonなのかルート環境なのかを注意して実行すること。(作業フォルダにembeddable pythonがあるかどうか。もしくはpipインストール後にpython -m pip list等でパッケージの中身を見て確認。)

pip、get-pip関係のエラーは下部のエラー対策箇所にまとめておきました

cmd
cd (embeddable pythonのフォルダ)
# 念の為、python.exeがあるディレクトリか確認。
dir
# リストの中にpython.exeがあれば、get-pipをインストール
python get-pip.py

これでpipが使えるようになりました。
ただし、

cmd
# NG
# pip install numpy
# OK
python -m pip install numpy
# エラーが出る場合は以下のようにオプションをつけて実行
python -m pip --no-cache-dir install -I -U numpy

のようにpython -m pip install (パッケージ名)の書式でなければなりません。

試しにMatplotlibとWxPython

試しにMatplotlibを使ってグラフを書きつつ、wxPythonで描画してみましょう。

Package

以下のようにパッケージをインストール

cmd
python -m pip install numpy
python -m pip install matplotlib
python -m pip install wxpython

テストスクリプト

テストスクリプトは下記URLから引用。
main.pyという名前でpython.exeと同じ階層に保存してください。

スクリプト引用元
python - Embedding a matplotlib figure inside a WxPython panel - Stack Overflow

main.py
#! env python
# -*- coding: utf-8 -*-

from numpy import arange, sin, pi
import matplotlib
matplotlib.use('WXAgg')

from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure

import wx

class CanvasPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.figure = Figure()
        self.axes = self.figure.add_subplot(111)
        self.canvas = FigureCanvas(self, -1, self.figure)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        self.SetSizer(self.sizer)
        self.Fit()

    def draw(self):
        t = arange(0.0, 3.0, 0.01)
        s = sin(2 * pi * t)
        self.axes.plot(t, s)


if __name__ == "__main__":
    app = wx.PySimpleApp()
    fr = wx.Frame(None, title='test')
    panel = CanvasPanel(fr)
    panel.draw()
    fr.Show()
    app.MainLoop()

バッチファイルを保存

同じ階層にバッチファイルをmain.cmdという名前で以下のように記載して保存。

main.cmd
rem このファイルの位置を作業ディレクトリに
cd /d %~dp0
rem main.pyを実行
python.exe main.py

main.cmdを実行するとグラフが描画される。

0.wyhsc17sc2.png

容量

ディレクトリの容量は 183 M程度になりました。
パッケージを含めると流石に容量が大きいですが、しかし、速度はPyInstallerより高速に動いている感じがします。

エラー対応

“python setup.py egg_info” failed with error code 1

python -m pip install パッケージ名

上記のようにパッケージを追加しようとしたときエラーが表示されることがあります。
このエラーの原因は、zipファイル内のlib2to3というパッケージの中身が展開出来ないことによるもののようです。
対応方法としては、
1.同封の「PythonNN.zip」を失敗した時の為にバックアップ、コピーしておく。
2.「PythonNN.zip」をフォルダとして展開。
3.「PythonNN」というフォルダ名から「PythonNN.zip」に変更。

“python setup.py egg_info” failed with error code 1
https://stackoverflow.com/questions/42962765/embedded-python-3-5-python-setup-py-egg-info-failed-with-error-code-1

解決しない場合

setuptoolsのバージョンをダウングレードして解決する場合があります。これは、2to3をサポートしなくなったことに由来するとのこと。

https://qiita.com/nkmr_RL/items/85edc2ee68c01ec5582e

python -m pip install setuptools==57.4.0

ERROR: Could not install packages due to an OSError: .....

こちらの記事に対策が書かれていました。

Python.h pythonNN.lib

Python.hが見つかりません や、 'python37.lib' を開くことができません。 といった旨のエラーが出た場合は、python -m pip install .... コマンドを実行する前にそのファイルが含まれるパスを以下のコマンドで追加します。

また、この手のエラーはコンパイラも必要となる場合もあります。 Visual Studioやbuild toolsをインストールしたりしましょう。

例えば、AnacondaやMinicondaのがインストールされている場合は、以下のようになります。(例はMiniconda)

SET INCLUDE=C:\ProgramData\Miniconda3\include;%INCLUDE%
SET LIB=C:\ProgramData\Miniconda3\libs;%LIB%

ModuleNotFoundError: No module named XXX

同じ階層にpyファイルがあり、それをimportしようとしてエラーが発生した場合、その発生箇所よりも前に、以下のスクリプトを追加する。

sys.path.append(os.path.dirname(os.path.abspath(sys.argv[0])))

公式
1. 他のアプリケーションへの Python の埋め込み — Python 3.6.4 ドキュメント
Python/C API リファレンスマニュアル — Python 3.6.4 ドキュメント

342
398
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
342
398