2019-11-11時点で知ってることについてまとめておこうと思います。

前提

  • 状況に応じて使い分けましょう
    • 2/3系の最新、ぐらいの大きなくくりでも問題ない人
    • 2.6環境を用意しなければいけないとき😔
    • Win環境でデータ解析した人で仮想環境とか考えなくてもいいのであれば全部入りのanacondaでもいいかもしれない
    • 標準でPythonが入っているLinux環境(Mac含む)では推奨しない
    • (仮想環境を使いこなせばanacondaのような環境作るは簡単)
  • 良質な仮想環境構築記事を見分けられるようになりましょう
  • お前を信じる仮想環境を信じましょう

最初にまとめ

さまざまな仮想環境ツールがありますが、基本的には、

  • PATHに仮想環境作成時のpythonのsymlink or 実体を追加する
  • sys.pathにsite-packagesを追加する

を実現するためのツールなので必要に応じて使い分けましょう。

その他にも、

  • pythonのバージョン管理も内包
  • (pip以外の)pythonのパッケージ管理も内包

があるので必要に応じて使い分けましょう。

筆者は以下を必要に応じて使い分けてるので、いずれにせよ pyenv はあると楽かも

  • pyenv + venv
  • pyenv-virtualenv
  • pyenv + pipenv

venv

公式の提供するモジュールです。3.3からデフォルトで使えます。

venv --- 仮想環境の作成 — Python 3.8.0 ドキュメント

※ 2.7等には入ってません

仮想環境を有効化する とは

venvでの仮想環境構築を通しておおまかな仮想環境有効化の動きを追ってみます。

$ python -V  # Python 2.7.10
$ python3 -V  # Python 3.6.8
$ pwd
/Users/yourusername/foobar

# python3 -m venv /path/to/new
$ python3 -m venv ./env

とすると /path/to/new なディレクトが作られます(ここでは env )。これが仮想環境のディレクトリになります。

treeコマンドで中身を見てみます。

$ tree -L 4
.
└── env
    ├── bin
    │   ├── activate
    │   ├── activate.csh
    │   ├── activate.fish
    │   ├── easy_install
    │   ├── easy_install-3.6
    │   ├── pip
    │   ├── pip3
    │   ├── pip3.6
    │   ├── python -> python3
    │   └── python3 -> /Users/yourusername/.anyenv/envs/pyenv/versions/3.6.8/bin/python3
    ├── include
    ├── lib
    │   └── python3.6
    │       └── site-packages
    └── pyvenv.cfg

./env/bin 以下に、venv実行時のpythonの実体に向いた symlink、や pipactivate 等があるのがわかります。

次に ./env/bin にある activate を実行し、仮想環境を有効化してみます。

activate ですが、OSはもちろん、shellごとにも異なったりするので(たとえば .fish とか .csh があるのが見えると思います)、公式ドキュメント参照のこと。

$ source ./env/bin/activate

さて何が変わったのでしょうか

env > $ python -V  # Python 3.6.8

まず、 python コマンド実行時のバージョンが python3 実行時のバージョンである 3.6.8 になっています。

仮想環境有効化の前後のPATHをみてみます。

env > $ echo $PATH
/Users/yourusername/foobar/env/bin:/usr/local/Cellar/zplug/2.4.2/bin:・・・

env > $ deactivate
/usr/local/Cellar/zplug/2.4.2/bin:・・・

仮想環境を有効化することで./env/binがPATHに追加されています。

これによって python 実行で参照しているバージョンが3.6.8になっているということがわかります。


さらに外部のパッケージの参照パスについて見てみます。

$ pip install requests
$ python
>>> import requests
>>> exit()
$ source ./env/bin/activate
env > $ python
>>> import requests
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'requests'

基本的に、仮想環境下では、元のpythonで入れたパッケージは参照することができません。

よって、仮想環境内のpipを通してパッケージをインストールすることで仮想環境内でパッケージをimportできるようになります。

$ source env/bin/activate
env > $ pip install requests
env > $ python 
>>> import requests

こちらも仮想環境の有効化前後で sys.path を比較してみます

env > $ python 
>>> import sys
>>> sys.path
['', '/Users/yourusername/.anyenv/envs/pyenv/versions/3.6.8/lib/python36.zip', '/Users/yourusername/.anyenv/envs/pyenv/versions/3.6.8/lib/python3.6', '/Users/yourusername/.anyenv/envs/pyenv/versions/3.6.8/lib/python3.6/lib-dynload', '/Users/yourusername/workspace_python/foobar/env/lib/python3.6/site-packages']
>>> exit()
env > $ deactivate
$ python
>>> import sys
>>> sys.path
['', '/Users/yourusername/.anyenv/envs/pyenv/versions/3.6.8/lib/python36.zip', '/Users/yourusername/.anyenv/envs/pyenv/versions/3.6.8/lib/python3.6', '/Users/yourusername/.anyenv/envs/pyenv/versions/3.6.8/lib/python3.6/lib-dynload']

こちらも仮想環境下の site-packages が追加されていることがわかります。


以上が 「仮想環境を有効化する」 と表現していることの実体です。

venv はシンプルな動きですし、プロジェクトのディレクトリ内に仮想環境を持てるというのは意味的にスッキリしています。基本的な仮想環境の動きを理解するのに最適かもしれません。

例えば、後述する pyenv-virtualenv 等は単一の名前空間に仮想環境を作るようなイメージなので、プロジェクトとの関連が薄いです。

注意点として、venvで作成した仮想環境ディレクトリはgit管理に含めないようにしましょう。絶対パスのsymlinkが含まれることからもそれらが他人の環境で動かないことがわかるかと思います。

virtualenv

pypa/virtualenv: Virtual Python Environment builder

こちらもシンプルです。

venvと生成物比較するとlib以下がsite-packages以外のファイルのsimlinkも持ってたり、pythonの実行ファイルもsimlinkでなく実体なので容量食ったりですが、使い方はほとんどvenvと同じです

$ pip install virtualenv
# virtualenv /path/to/new
$ virtualenv ./env
$ source ./env/bin/activate

pyenv-virtualenv

pyenv は複数バージョンのpythonを入れたい場合のパッケージマネジャー的なツールです。

様々なversionのPythonを始め、Java実装であるJythonはもちろんanacondaまでもinstall可能です。

.python-version というファイルを見てpythonのバージョンを変えます。

$ pyenv local system
$ python -V
Python 2.7.10 

$ cat .python-version
system

# 3.6.8をpyenvからinstallしたい場合
$ pyenv install 3.7.0

# anyenv経由でpyenv使ってるのでちょっとパスが特殊ですが利用イメージ
$ pyenv versions
* system
  3.7.0 (set by /Users/yourusername/.anyenv/envs/pyenv/version)

$ pyenv global 3.7.0
$ python -V
Python 3.7.0

$ pyenv versions
  system
* 3.7.0 (set by /Users/yourusername/.anyenv/envs/pyenv/version)

pyenv-virtualenvpyenvvirtualenv を使ったwrapperです

pyenv に乗っかりつつ virtualenv できる感じです

# インストールしたバージョンから仮想環境を作る
$ pyenv virtualenv 3.7.0 3.7.0-env

$ pyenv versions
  system
  3.7.0 (set by /Users/yourusername/.anyenv/envs/pyenv/version)
  3.7.0/envs/3.7.0-env
* 3.7.0-env

# 有効化する
$ pyenv activate 3.7.0-env

# プロジェクトディレクトリでこれでもOK
$ pyenv local 3.7.0-env

やはり .python-version はgitに含めないようにしましょう

venvvirtualenv との違いは、

  • 仮想環境ディレクトリの作成先が $HOME/.pyenv/versions/[your_env]配下
  • 環境毎に名前をつける必要がある

の2点です。

例えばPyCharmでインタプリタのパスを指定するとき、 virtualenvvenv は勝手に読んでくれたりしますが、 pyenv-virtualenv の場合 .pyenv 配下を漁る必要があり少し面倒です。

また、仮想環境ディレクトリはディレクトリの場所に依存しないので汎用的な環境を作っておくと使いまわせたりします。

pipenv

pipenvpip をwrapし、 npm のようなパッケージ管理の機能 + virtualenv or venv を一元管理するツールです。

Pipenv: 人間のためのPython開発ワークフロー — pipenv 2018.11.27.dev0 ドキュメント

公式も推奨しています。

PyCon 2018の作者の講演が参考になります

Kenneth Reitz - Pipenv: The Future of Python Dependency Management - PyCon 2018 - YouTube

氏が問題点として上げているのが、

  • requirements.txtによるパッケージ管理はlockfileの機構がない
  • 依存関係が1ファイルにすべて保存されているため、本当に必要とされているパッケージがわからない

という点です。

例えば、 pip install flask としたとき、真にプロジェクトに必要なパッケージは flask のみであるはずが、flask の依存パッケージである clickjinja2 までもを requirements.txt で管理する必要があります。

これを pipenv では必要なパッケージは Pipfile というファイルに、その依存関係を Pipfile.lock と分けることでこれを解決しています。

また、開発でのみ必要なパッケージ -例えば pytest など- の管理も可能です。

virtualenv によって解決したい領域とは違うため、「仮想環境構築ツールとして語るのはどうなの?」と思うかもしれませんが、 pipenv はとにかく多機能で、仮想環境作成の機能や、npmライクなscript管理も可能です(仮想環境構築、という点でいうとオーバースペックかも)

仮想環境の構築には virtualenv or venv を使うため、それらの動きをすでに理解している方や、 requirements.txt によるパッケージ管理に問題を抱えている方には有用かと思います。

# 仮想環境を作成
# 色んなバージョン指定の方法
# このときpyenvが入っていると、指定したバージョンが未インストールでもインストール可能
$ pipenv --three
$ pipenv --python 3.6
$ pipenv --python 3.6.8

# 仮想環境を有効化する
$ pipenv shell

仮想環境の位置ですが、デフォルトではホームディレクトリの.local配下に作られます。PIPENV_VENV_IN_PROJECT の環境変数をtrueにしておくとカレントディレクトリのの.venv配下に作られます。

参考: Pipenvと仮想環境 — pipenv 2018.11.27.dev0 ドキュメント


パッケージのinstallはpipenv経由で行います。このとき自動的にPipfile / Pipfile.lockへ書き込まれます。

$ pipenv install flask
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
flask = "*"

[requires]
python_version = "3.6" 

開発用のパッケージも同様です --dev or -d をつけるだけです

$ pipenv install --dev pytest 
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]
pytes = "*"

[packages]
flask = "*"

[requires]
python_version = "3.6" 

Pipfile / Piplock.fileはgit管理に含めましょう。Pipfileがあるディレクトリで pipenv install とすると、Pipfileに沿ったバージョンの仮想環境とパッケージのインストールが可能です。ここもnpmライクですね。

script管理

Pipenvの進んだ使い方 — pipenv 2018.11.27.dev0 ドキュメント | 独自のスクリプトショートカット

開発をしているとサーバの起動コマンドが複数必要なケースがあると思います。

例えばDjangoでrunserverするときに参照するsettings.pyを変えたいときなどが考えられますが、コマンドが長くなりがちですし、READMEで管理するのも面倒かと思います。

こういったスクリプトをPipenvのscriptsに登録することで、コマンドの抽象化ができ、プロジェクトで必要なコマンドが一覧で分かります。

# pipenv run [script_name]
$ pipenv run start
[scripts]
start = "python manage.py runserver --settings config.setting.dev"
format = "black ."

scriptで実行されるpythonはm仮想環境を有効化しているかに関わらず、仮想環境下で実行されます。

また .env というファイルをプロジェクトに作成すると pipenv run pipenv shell 時に自動で読み込みます。

Pipenvの進んだ使い方 — pipenv 2018.11.27.dev0 ドキュメント | .env の自動読み込み

Poetry

コマンド見る感じ使用感はpipenvっぽい?要調査

sdispater/poetry: Python dependency management and packaging made easy.

まとめ

さまざまな仮想環境ツールがありますが、基本的には、

  • PATHに仮想環境作成時のpythonのsymlinkを追加する
  • sys.pathにsite-packagesを追加する

を実現するためのツール。

その他にも、

  • pythonのバージョン管理も内包
  • (pip以外の)pythonのパッケージ管理も内包

があるので必要に応じて使い分けましょう。