pythonの仮想化環境についてのまとめ

仮想化とは何か?

特定のプロジェクトや特定のディレクトリに限定したpythonの環境をつくることを仮想化という。 またpythonの環境には大きく分けて2つある。 一つはpython本体で、python2.7とかpython3.6とか、どのversionのpythonを使うかということ。 もう一つは、packageで、どこのディレクトリのpackageを使うかということ。

要するに仮想化をするとは、 特定のプロジェクト(ディレクトリ)で どのpython本体とどのpackageディレクトリを使うかを決めるだけの話。

どのプログラムを使うかというのは、 要するに実行するときのPATHを決めるというだけの話である。 だから仮想化を理解するにはPATHのことを理解する必要がある。

仮想化ではPATHが一番大事(たぶん)

そもそもPATHとは環境変数の一つである。 環境変数とは、OSの機能で、タスクが動作するのに必要な情報を書き込む仕組みである。 macではターミナルでprintenvとコマンドを打てば設定されている環境変数が表示される。

TERM_PROGRAM=Apple_Terminal
SHELL=/bin/bash
USER=kapi
SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.t748VX0p9g/Listeners
PATH=/usr/local/opt/llvm/bin:/Users/kapi/.pyenv/shims:/Users/kapi/.pyenv/shims:/Users/kapi/.pyenv/bin:/Applications/MacVim.app/Contents/MacOS:/usr/local/bin:/Users/kapi/.nvm/versions/node/v9.3.0/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/Applications/Wireshark.app/Contents/MacOS:Users/kapi/.local/bin
HOME=/Users/kapi
...

USERやSHELLなどが設定されているのがわかる。 で、PATHとは環境変数の一つであり、コマンドがある場所を列挙した変数のことである。

ターミナルでlsコマンドを打つとファイルが表示されるのはlsという実行ファイルを見つけて実行しているからだが、 もしPATH情報がないと、実行ファイルがどこにあるかを全ファイルから探す必要があり、めちゃくちゃ時間がかかってしまう。 もしくは、lsと打つのではなく、/bin/ls のように実行ファイルがある場所を毎回指定する必要がある(フルパスという)

PATHがあれば、PATHの場所だけコマンドを探せばいい。 また実行ファイルはPATHのあるところに置くか、実行ファイルの場所をPATHに追加したらよい。

なお、PATHだけを表示させるには echo $PATH でよく、 PATHは .bashrc または .bash_profile に記載されている。

複数の実行ファイルがあるときの挙動

そして、ここが仮想化で重要なところだが、 例えばpythonインタプリタの実行ファイルが複数あるとき、どれが実行されるのか? それはPATHが書かれている順番に探していって、先に見つかった方である。 PATHにpython2.7のpathが書かれていればpython2.7インタプリタが実行される。 と、いうことはPATHにかかれている順番をコントロールしたら、任意のpythonを実行できるのか? YES. pyenvではまさにそれをしている。

参考 [Mac]環境変数PATHの設定・変更・追加・確認・順番入れ替えの方法 | Gabekore Garage

pyenvのお仕事

pyenvの大きな仕事は2点

pyenvがやらないこと

基本的にコマンドを先回りするので特にactivateなどは必要ない。 ディレクトリに設定ファイルを置いてそれを先回りして読むことをしている。 詳しくはgitをみるべし。 pyenv/pyenv: Simple Python version management

pythonインタプリタのインストール

pyenv install -list でインストール可能なリストを表示できる。 pyenvを最新にしておかないとだめだった気がする。 (pyenv update や pyenvのルートディレクトリで git pullする) anacondaなんかもインストール可能

ディレクトリごとにpythonの環境を分ける

pyenv local 環境名 とすると、そのディレクトリに .python-version が作られ、そこに環境名が書かれている。 で、このディレクトリでpython関係のコマンドを打つと、その環境のものが使われる。 例えばpipをしたら、.python-versionのところにだけインストールされる。 pyenv versions としたら、どのversionがインストールされていて、そのディレクトリではどのversionを使うかが示される また、どこの.python-versionファイルによって設定されたかも表示される

MacBook-Pro:tmp kapi$ pyenv versions
  system
  2.7.16
  3.6.9
  3.7.4
* anaconda3-5.3.1 (set by /Users/kapi/Documents/GitHub/anaconda3-5.3.1/.python-version)

localでの設定がなければ、ディレクトリをさかのぼり、pyenvのrootにいく。 rootではgloalの設定がある。 それもなければ、systemのpythonを使うことになる。

なお実際にpyenvでやっていることは pyenv、pyenv-virtualenv、venv、Anaconda、Pipenv。私はPipenvを使う。 - Qiita から

  • $PATHの先頭にPYENV/shimsを挿入し、あらゆるPython系コマンドへのアクセスに割り込む。
  • いま動かすべきPythonインタプリタを探す。
  • 受け取ったコマンド(例えばpython main.py、pip3 install -r)を、その探したインタプリタに投げる。 すなわち、諸々のコマンドを適切な窓口に振り分ける、受付係を引き受けてくれるのである。 適切なインタプリタを探すために、pyenvは以下の操作を行う:
  • もし環境変数PYTHON_VERSIONが設定されていれば、そのバージョンに従う。
  • そうでなければ、今のディレクトリから順に親ディレクトリを遡っていく。その途中で.python_versionファイルが見つかれば、そこに書かれてい>るバージョンに従う。
  • ルートまで遡っても見つからなければ、グローバルに設定されているバージョンに従う。 そういうわけで、which pythonをしても、pyenvの管理化では、 /Users/kapi/.pyenv/shims/python がどこも表示される

pyenvはpythonインタプリタとpackageは分けることは出来ない

インタプリタとpackageがセットになっていると考えていい。 pipをしたら.python-versionで設定されているところにインストールされるだけである。 だから、python3.7.4で2つのパッケージ環境を作ることはできない。 同じpythonインタプリタでパッケージ環境を分けるにはvenvをつかう必要がある。

venvのお仕事

venvがやるのはパッケージ環境の仮想化である。 環境を作ると、そのディレクトリ下に仮想環境の実行ファイルがおかれ、 どのパッケージのディレクトリを使うか、のpathを決めている。

ただし、pyenvと違ってコマンドを乗っ取るわけではないので、 仮想環境を使うには activateが必要になる。 source [環境作ったディレクトリ]/bin/activate でactivateする。(deactivate としたらdeactivateされる) activateした状態でpipすると、指定されたほうのsite-packagesにinstallされる

ちなみにvenvは公式あまりわかりやすくなかった venv --- 仮想環境の作成 — Python 3.7.4 ドキュメント こっちのqiitaの方がわかりやすかった venv: Python 仮想環境管理 - Qiita

venvのやっていることを確認するにはpythonインタプリタを起動してsite-packagesのパスを確認したらいい。

これは、venvでない、環境でのpythonのimportのpath

MacBook-Pro:~ kapi$ python
Python 3.7.4 (default, Aug 14 2019, 08:40:34) 
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> import pprint
>>> pprint.pprint(sys.path)
['',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python37.zip',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python3.7',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python3.7/lib-dynload',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python3.7/site-packages']
>>> 

こっちは、python -m venv try_venv で作った環境をactivateして作った環境でのpythonのimportのpath

(try_venv) MacBook-Pro:~ kapi$ python
Python 3.7.4 (default, Aug 14 2019, 08:40:34) 
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> import pprint
>>> pprint.pprint(sys.path)
['',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python37.zip',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python3.7',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python3.7/lib-dynload',
 '/Users/kapi/Documents/GitHub/try/try_venv/lib/python3.7/site-packages']
>>> 

見て分かる通り、python本体は共通で、site-packagesだけが異なる。 これがvenvがやっていること。 (try_venv)という環境でpipをすると、こっちにだけ、installされることになる。なるほど〜〜。

pipenvについて

これはちょっとわかりにくい より抽象化されている。 ディレクトリごとの環境が基本みたいだ。 またpythonはおそらく共通で使っているということだな。 で、activateするんじゃなくて、 pipenvコマンドをつかってinstallやら実行やらをすると。 多分、そのディレクトリにパッケージのパスがかいてあって、それを探してそう。 また使うときに調べよう。

で、どう使うか?

よく考えたら別にvenvも使わなかった。。。 アプリ開発するわけでもない とはいえ、環境を明示的にしておいた方がいいのも確かなので、 cvの勉強用にはpyenvでanacondaをいれておく。 具体的にはpyenvでanacondaをインストールして、
特定のフォルダ以下で
pyenv local anacondaxxx としておいた。