追記:18-10-31 パッケージのインストールエラーに対応する方法を追記
Pythonを独立したExeファイルとして書き出すPyInstallerの記事を以前書きました。
しかし、この方法よりも実行がより高速、より軽量な方法がありました。
embeddable pythonと呼ばれるもので、Windows限定ではありますが、ダウンロードしてきた圧縮状態なら 7 MB、解凍しても 14 MB程度のPython実行環境です。
もちろんこのままではパッケージを使えませんので、そのパッケージ導入方法も合わせて記載します。
ダウンロード
上記ページにあるWindows x86-64 embeddable zip fileをダウンロードして解凍。
もしくは、WindowsPowerShellで以下を実行。(wgetが使えるため楽だから)
# 作業用フォルダへ移動(適当なところで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
最小構成
pythonNN.dll、pythonNN.zip、vcruntime140.dllがあればPythonは動作します。
python.exeは、pythonNN.dllへ引数を送るだけの実行ファイルですので、独自C++に組み込むときは必要ありません。
(pythonNN.zipは圧縮ファイルですが、このままで機能しますので解凍してはいけません。 フォルダ名さえ気をつけていれば解凍しても使えます。詳しくは下部の「エラー対応」項目を参照)
今回は他の構成ファイルを使うのでそのままにしておいてください。
PIP
pythonNN._pth
ファイルを修正。
そのままではget-pip.py
が使えないので、少し手直しをする必要があります。
ダウンロードしてきたファイルの中にあるpython36._pth
というファイルをエディタで開いて、
# import site
を、
import site
とコメント解除する必要があります。
python 3.6 embed cannot get pip · Issue #7 · pypa/get-pip · GitHub
それから、https://bootstrap.pypa.io/get-pip.py
のファイルをダウンロードするか、ダウンロード処理を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"
コマンドプロンプトで作業
これでget-pip.py
が使えるのでインストール作業をしますが、
ここからはコマンドプロンプトで作業します。(PowerShellではモジュールエラーで実行出来なかった。)
要注意点
get-pip
に限らず、ルート環境にpythonがインストールされているときはembeddable pythonなのかルート環境なのかを注意して実行すること。(作業フォルダにembeddable pythonがあるかどうか。もしくはpip
インストール後にpython -m pip list
等でパッケージの中身を見て確認。)
cd (embeddable pythonのフォルダ)
# 念の為、python.exeがあるディレクトリか確認。
dir
# リストの中にpython.exeがあれば、get-pipをインストール
python get-pip.py
これでpip
が使えるようになりました。
ただし、
# NG
# pip install numpy
# OK
python -m pip install numpy
のようにpython -m pip install (パッケージ名)
の書式でなければなりません。
試しにMatplotlibとWxPython
試しにMatplotlibを使ってグラフを書きつつ、wxPythonで描画してみましょう。
Package
以下のようにパッケージをインストール
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
#! 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
という名前で以下のように記載して保存。
# このファイルの位置を作業ディレクトリに
cd /d %~dp0
# main.pyを実行
python.exe main.py
main.cmd
を実行するとグラフが描画される。
容量
ディレクトリの容量は 183 M程度になりました。
パッケージを含めると流石に容量が大きいですが、しかし、速度はPyInstallerより高速に動いている感じがします。
エラー対応
pipでエラー
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
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 ドキュメント
vcruntime140.dll(Visual C++ Redistributable for Visual Studio 2015)の取得元をメモしておきます
私の環境では、
python -m pip install
の際にModuleNotFoundError: No module named 'pip'
というエラーが発生しました。この場合は、pythonXX._pth
ファイルに./Lib/site-packages
と追記すると良いようです。また、embeddable に付属していない標準モジュール
tkinter
は、ここのようにして、使えるようにできました。これは経験則に基づくのですが、pythonのない環境のPCに配布するためのpythonアプリを作成する、一つのpythonバージョンに依存する(更新を行わない前提)等といった理由がなければembeddable zip fileを使用するのはおすすめしません。
(特に勉強目的でpythonをインストールするならおとなしくインストーラを使用したほうがいいです。)
embeddable zip fileは組込み等の業務目的またはpythonの仕組みを理解しており、セキュリティ等も含めた各種設定を全て自分で出来るといった上級者向けの配布パッケージになります。(情報も散在していおり、結構苦労しました・・・)
理由として、python公式ドキュメントではコマンドライン引数にファイルを指定して呼び出した場合、
(python C:\~~\main.py のようなコマンド)sys.path[0]にコマンドライン引数に指定したファイルのディレクトリパスをモジュール参照先設定として追加すると記述があり、
[https://docs.python.org/ja/3/library/sys.html?highlight=sys%20path]
インストーラ版ではその記載通りに動くのですが、embeddable zip fileではコマンドライン引数に指定したファイルのディレクトリをモジュール参照パスに指定してません。
(pip部分で起こっている問題がまさにそれです。)
[https://docs.python.org/ja/3/using/windows.html]
上記マニュアルの(3.8. モジュールの検索)にその挙動について説明があります。
embeddable zip fileでは[python〇〇._pth]があると隔離モードでの起動になるため全て自分でモジュール参照設定を明記する必要があります。
※隔離モードでは全てのレジストリと環境変数(PYTHONPATH)は無視されます。
これの対応方法としては、
①ソースコードに参照するモジュールのパスを一つ一つ設定する
②python.exeの含まれたディレクトリに[適当な名前.pth]を作成し、その中にpython.exeを起動した際に必ず参照させたいディレクトリパスを記述する
③python〇〇.__pthファイルを削除する(隔離モードの解除)
等がありますが、それぞれデメリットもあります。
①であれば自身のソースがどのモジュールを参照するか全て把握し、網羅して記載する必要があります(pipしたパッケージ、自作モジュール等)
②であれば小さなpythonプログラムが増える度、いちいちpthファイルに記載する必要がある上、同名のファイル、変数名が存在する場合多重定義エラーが発生します。
③を削除した場合はインストーラ版と同じ挙動をしますが、細かい設定ができなくなります。(embeddable zip file使う時点で細かい設定をすると言う前提になるのでそれなら最初からインストーラを使用するべきです。)
それ以外にもインストーラ版との違いとしては
・py.exeが含まれない
というのがあり、
py.exeはpythonランチャーと呼ばれており、複数のpythonバージョンを管理し、ソースコードに合わせて適切なバージョンを起動してくれるものになります。(新バージョンを試す目的で複数バージョンをインストールするとき等に効果を発揮します。)
インストーラ版ではインストール時に特殊な設定をしないとpythonインストール先がユーザー固有のディレクトリ(WindowsならAppData以下)にしかインストール出来ませんが、それはセキュリティ問題に対応する理由があります。
(全ユーザーがアクセス出来る様なディレクトリにpython.exeをインストールした場合、悪意あるスクリプトファイルやスクリプトを実行できる為)
それらも踏まえて全部自身で管理できる場合にだけ使用してください。
pythonではインストーラが複数存在しますが、含まれているものの違い、挙動の違いも把握しないといけません。(pythonに限りませんが・・・)