pyenv、pyenv-virtualenv、venv、Anaconda、Pipenv。私はPipenvを使う。

# はじめに

> If a thing can be done adequately by means of one, it is superfluous to do it by means of several; for we observe that nature does not employ two instruments if one suffices. 
> -- Thomas Aquinas (1225-1274)

[`pyenv-virtualenv`](https://github.com/pyenv/pyenv-virtualenv) というツールを、私は長らく使ってきた。
`pyenv-virtualenv`は素晴らしいツールで、私はプロジェクトを切り替えるとき、`Python`の環境について一切悩むことなくただ`cd`するだけでよかったし、他の環境で作業するときも、`requirements.txt`を通じて全く問題なく環境をコピーすることができていた。
私の`.zshrc`には、「`Pyenv-virtualenv`がなければ勝手に`git clone`してパスを通し、`pyenv-init`を実行する」という処理が堂々と鎮座していた。

しかし、時代は変わった。
[`Pipenv`](https://github.com/pypa/pipenv)を試したとき、私の中で`pyenv-virtualenv`は終わりを迎えた。
`Pyenv`は私にとってブルドーザーだったし、`pyenv-virtualenv`は私にとって時間喰い虫だったのだ。

今日はまだ`pyenv-virtualenv`に囚われている亡霊のあなたに向けて、`Pipenv`への転生の門を開こうと思う。

# `Python`を取り巻く、環境戦国時代

この記事を読むために、まずは`Python`における環境構築手法をいくつか知っておかねばならないだろう。

`Python`インタプリタには、様々なバージョンがある。もしあなたが考古学者なら、`Python2`系の存在を知っていることだろう。3系の中でも互換性のない変更はたくさん行われているし、あるいは`PyPy`や`IronPython`といった（よく知られた）`Python`とは異なる実装のインタプリタもある。
つまり、我々はたくさんの`Python`を持っていて、それらをプロジェクトに応じて適切に切り替えなければならない。

また、`Python`には様々なパッケージがある。開発者はこれらを好き勝手にインストールして使うわけだが、もちろんこれらのパッケージにもバージョンや互換性の問題は存在する。「このプロジェクトは、このパッケージをインストールしないと動きません。ただし、バージョンxx以上yy以下です。」という注意書きがすべてのプロジェクトに存在しており、それらをうまく扱うためにはパッケージのバージョンを切り替えてくれるツールが当然必要だ。

そういうわけで、我々は2つのものごとについて仮想環境を必要としている。
すなわち、`Python`インタプリタ本体と、パッケージ群である。

## まとめ

||インタプリタ切替|パッケージ切替|パッケージインストール|
|:-:|:-:|:-:|:-:|
|pyenv|○|||
|virtualenv|○|○||
|pip|||○|
|venv||○||
|pyenv-virtualenv|○|○||
|virtualenvwrapper||○||
|Anaconda|○|○|△|
|Pipenv||○|◎|


## pyenv

https://github.com/pyenv/pyenv

`pyenv`は、前述した2つのうち前者のみを担当する仮想環境マネージャである。

`pyenv`のやることは以下の通りである：

+ `$PATH`の先頭に`PYENV/shims`を挿入し、あらゆる`Python`系コマンドへのアクセスに割り込む。
+ いま動かすべき`Python`インタプリタを探す。
+ 受け取ったコマンド（例えば`python main.py`、`pip3 install -r`）を、その探したインタプリタに投げる。

すなわち、諸々のコマンドを適切な窓口に振り分ける、受付係を引き受けてくれるのである。

適切なインタプリタを探すために、`pyenv`は以下の操作を行う：

+ もし環境変数`PYTHON_VERSION`が設定されていれば、そのバージョンに従う。
+ そうでなければ、今のディレクトリから順に親ディレクトリを遡っていく。その途中で`.python_version`ファイルが見つかれば、そこに書かれているバージョンに従う。
+ ルートまで遡っても見つからなければ、グローバルに設定されているバージョンに従う。

こうすることで、今いるプロジェクトにとって最も適切な`Python`を起動することができるのである。

結局、`Pyenv`を用いる利点は以下の通り：

+ 状況に応じて適切な`Python`インタプリタを、手動で切り替えることなく用いられる。
+ いろいろな`Python`インタプリタを、コマンド一発でインストール・アンインストールできる。

## virtualenv

https://github.com/pypa/virtualenv

`virtualenv`は、前述した2つの両方、すなわち`Python`インタプリタとパッケージ群を同時に切り替えられる仮想環境マネージャである。

`virtualenv`は、`ENV/`以下に`Python`インタプリタ、インストールされたパッケージなどすべてを保存している。そして、`source ENV/bin/activate`を行うと（これが`virtualenv`を使うための魔法の呪文である）、その仮想環境のフォルダにパスが通る。これ以上のことは何もしない。

いちいち仮想環境の場所を探して`activate`しなければならないという面倒臭さはあるものの、動作は単純明快であり、環境が分離されていることもわかりやすい。ただし、`pyenv`とは異なり、新しい`Python`をインストールしたりはできないので注意が必要である。あくまでも`$PATH`を探して`Python`を見つけ、そのインタプリタをコピーするだけである。

結局、`virtualenv`を用いる利点は以下の通りである：

+ `Python`インタプリタとパッケージの組み合わせを好きなだけ作り、分離することができる。
+ それらを`activate`で切り替えることができる。


## pip

https://pip.pypa.io/en/stable/

`pip`は、`Python`公式のパッケージ管理ツールである。

`Python`を知っている人であれば、この並びに`pip`が居るのはおかしいと思うかもしれない。
実際、`pip`は`Python`インタプリタを切り替えてはくれないし、パッケージの仮想環境を作ってくれるわけでもない。

`pip`がすることは、`Python`のパッケージを探し、適切なバージョンのパッケージを、依存関係を解決しながらインストールすることである。
`venv`や`pyenv-virtualenv`など、多くの仮想環境ツールは`pip`を使ってパッケージをインストールすることを想定している。

`pip`でインストールしたパッケージは、`requirements.txt`という形で共有することができる。
この`requirements.txt`には、必要に駆られてインストールしたパッケージのほか、依存関係の解決に必要だったパッケージ、開発時に必要だったlinterパッケージなどもすべて記述されている。
別の環境でそのプロジェクトを実行したければ、適当な仮想環境の下で`pip install -r requirements.txt`とすれば、それらのパッケージが全てインストールされ、元と同じ環境で実行できるというわけだ。

ここで`pip`を紹介した理由は、もちろん、このあと`pip`の仕事を代替するツールが登場するからである。



## pyenv-virtualenv

https://github.com/pyenv/pyenv-virtualenv

`pyenv-virtualenv`は、`pyenv`のシステムに乗っかりつつ、`virtualenv`を用いることができるツールである。
`pyenv`とも`virtualenv`とも別物である点に注意されたい。

`pyenv-virtualenv`では、`pyenv`と同様のインターフェースで`virtualenv`を用いることができる。しかも、`pyenv`のように、状況に応じて適切な仮想環境を、手動で切り替えることなく用いることができる。

すなわち、`virtualenv`では

```console
~$ cd project1  # Projectに移動
~/project1$ python -V
python 2.7.10  # まだ切り替わっていない
~/project1$ source ~/.virtualenv/project1/bin/activate  # 仮想環境をアクティベート
~/project1 (project1)$ python -V
python 3.7.3  # 切り替わった
```

というような処理を踏んでいたのが、

```console
~$ cd project1  # Projectに移動
~/project1 (project1)$ python -V
python 3.7.3  # もう切り替わった
~/project1 (project1)$ ls -a
. .. .python_version  # .python_version に仮想環境の名前が書かれている
```

と、`cd`するだけで仮想環境の切り替えが済んでいるのである。

でも、どうやって？

実は、`pyenv-virtualenv`は、あらゆるコマンドのあとに`activate`のチェックを行っている。
先述した、「今のディレクトリから順に親ディレクトリを遡っていく。その途中で`.python_version`ファイルが見つかれば、そこに書かれているバージョンに従う。」を、すべてのコマンドのあとに行っているのである。
当然、あらゆるコマンドの実行速度は目に見えて低下する。
activationの手間を省くために、かなりのコストを支払っているのである。

結局、`pyenv-virtualenv`を用いる利点は以下の通りである：

+ `pyenv`と同じインターフェースで、`virtualenv`を用いることができる。
+ `virtualenv`で用いる`Python`に、`pyenv`でインストールした`python`を用いることができる。
+ `virtualenv`の`activate`によるバージョンの切り替えを、手動で切り替えることなく用いられる。

## venv (旧称: pyvenv)

https://docs.python.org/ja/3/library/venv.html

`venv`は、`Python`公式が提供する仮想環境マネージャである。
その挙動は`virtualenv`とあまり変わらない。

公式が提供しているのだし、`virtualenv`を生で使うくらいならこっちを使うのがいいのではないだろうか。

## virtualenvwrapper

https://bitbucket.org/virtualenvwrapper/virtualenvwrapper

`virtualenvwrapper`は、何かと使い勝手の悪い`virtualenv`の動作を隠蔽し、変なことをしないようにするラッパーである。
使ったことがないのであんまりわからないが、おとなしく`venv`を使っておけばいいと思う。


## Anaconda (, miniconda, conda)

https://www.anaconda.com/

`Anaconda`は、`Python`インタプリタのインストールから仮想環境の作成、パッケージのインストール、その他の環境構築などなど、`Python`に纏わるあらゆることをすべて一手に引き受けるスーパーブルドーザーである。
あらゆることを引き受けるので、ここで述べているようなインタプリタのインストールや切り替え、パッケージの切り替えなども当然引き受けてくれる。

もちろん、`Anaconda`には、`pip`を代替する機能もある。例えば`pip`では`pip install mypy`とするところ、`conda`では`conda install mypy`などとすることで`mypy`パッケージをインストールすることができる。
また、このパッケージ管理システムと`Python`インタプリタだけを備えた最小限の`Anaconda`が、`miniconda`として配布されている。

`Anaconda`を使うなら、`Python`に関するあらゆることはすべて`Anaconda`に任せてしまうという気持ちが必要だ。
`Anaconda`の担当範囲があまりにも大きすぎて、他のどんな仮想環境ツールとも競合してしまうからである。
特に、`Anaconda`のパッケージ管理が`pip`と異なることで、「ほしいパッケージのバージョンが`conda`にない！ でもいまさら`pip`は使えない……」ということが結構あったりするのだ。その点は注意しなければならない。

何にせよ、どこまでが`Anaconda`の支配領域なのかを明確に理解できるようになるまでは、`Anaconda`を使うべきではない、と個人的には思う次第だ。

## Pipenv

https://github.com/pypa/pipenv

`Pipenv`は、`virtualenv`の仕事に加えて、`Pipfile`という次世代のパッケージ管理システムを実装したツールである。
`Pipenv`は仮想環境を作成するだけでなく、`pip`と同じようにパッケージ管理を行うことができる。その際、`pip`とは異なり、「何が要求されたパッケージで、何が依存関係の解決に必要だったパッケージなのか」を記録することができる。
これによって、どのパッケージがどのパッケージによって要求され、どのパッケージによってバージョン固定されたのかがわかり、パッケージリストがきれいになるのである。

`Python`に馴染みがない人にとっては、あまり利点がわからないかもしれない。
実際、あまり開発をしないのであれば、`venv`で十分なのではないかという気もする。

あと、これは個人的な主観なのだが、インターフェースが非常にわかりやすい。
新しいプロジェクトで仮想環境を作りたければ`pipenv install`とするだけ、`git clone`してきたプロジェクトに置いてあった`Pipfile`を読み込むときも`pipenv install`とするだけ、`pip`の代わりに使うときも`pipenv install <package-name>`と`pip`を`pipenv`に置き換えるだけ。大変わかりやすい。


## まとめ

||インタプリタ切替|パッケージ切替|パッケージインストール|
|:-:|:-:|:-:|:-:|
|pyenv|○|||
|virtualenv|○|○||
|pip|||○|
|venv||○||
|pyenv-virtualenv|○|○||
|virtualenvwrapper||○||
|Anaconda|○|○|△|
|Pipenv||○|◎|


# pyenv-virtualenvをやめ、Pipenvを使う

以上の仮想環境ツールを比較した上で、私は`pyenv-virtualenv`をやめ、`Pipenv`を使うことにした。

`pyenv-virtualenv`をやめたことで、私のshellは爆速になった。もう二度と`pyenv-virtualenv`を使うつもりはない。

`Pipenv`はあくまでも仮想環境マネージャなので、`Python`自体のインストールは別の方法で行う必要がある。
`pyenv`に任せてもいいのだが（実際、`Pipenv`には`pyenv`と協調する仕組みがある）、`pyenv`のやってくれることのうち「状況に応じて適切な`Python`インタプリタを用いられる」という部分は`Pipenv`がすでに担当しており、仕事が被ってしまうと思ったので、`Python`は手動でインストールすることにした。

実際、普通の人間に必要な`Python`は最新バージョンのstableな`python`（現時点では3.7.3）だけであり、あとは時々3.6や3.5、考古学者のために2.7が必要になるくらいである。`Pyenv`のような強いシステムの出る幕はたぶんない。

`Pipenv`のインストールは単純で、[公式ページ](https://pipenv-ja.readthedocs.io/ja/translate-ja/)に書かれている通り、`brew`や`apt`で入れたり、あるいは`python -m pip install --user pipenv`とするだけである。

新しくプロジェクトを立ち上げる際には、

```console
~$ mkdir project1  # Projectを作成
~$ cd project1  # Projectに移動
~/project1$ pipenv install  # 仮想環境を作成
~/project1$ pipenv shell  # 仮想環境に入る
```

とすればよい。
次からは単に`pipenv shell`とするだけである。


# おわりに

この記事では、`pyenv`、`virtualenv`、`pip`、`venv`、`pyenv-virtualenv`、`virtualenvwrapper`、`Anaconda`、`Pipenv`を並べ、それらの特性について軽くまとめた。
この記事が皆さんの`Python`ライフの一助となれば幸いである。

`Pipenv`流行れ。