🐍

Pythonの開発環境の3つの観点をおさえよう

2023/09/06に公開

先日 "Python の「仮想環境」を完全に理解しよう" というスライドを公開したらかなり反響がありました。

Python の開発環境の構築は、正直言ってかなり複雑だと思います。
pip・venv・pyenv・Pipenv などなど、似たような名前・似たような役割のツールがたくさん登場して、最初は全然意味が分かりません。
慣れればなんとかなるのですが、慣れるまではかなり苦しいです。

このようにとても難解であるにも関わらず、Python は機械学習などでよく使われることから、避けられないことも多いです。

そこでこの記事には、「そもそも Python の開発環境にはどんな観点があるんだ?このツールはなにを解決してくれるんだ?」という話をまとめます。

Python の開発環境の 3 つの観点とそのためのツールたち

Python の開発環境を構築するうえで、大きく以下の 3 つの観点があります。[1]

  • Python のバージョン指定
  • 仮想環境
  • パッケージ管理

上記の 3 つの観点と、ツールの対応を表にまとめました。

Python のバージョン指定 仮想環境 パッケージ管理
pip × ×
venv × ×
pyenv × ×
asdf × ×
Docker (Dev Container) ×
Pipenv ×
Poetry ×
Rye

○ ... この目的で使える
△ ... この目的で使えるが一部難点がある
× ... この目的で使えない

ツールの組み合わせの例

Python の開発環境を構築するときは、上記の 3 つの観点に対応づけてツールを選択します

例えば、pyenv + venv + pip というのは定番の(ただし一部難点もある)選択肢です。

Python のバージョン指定 仮想環境 パッケージ管理
pip × ×
venv × ×
pyenv × ×

私は asdf と Poetry をよく使います。

Python のバージョン指定 仮想環境 パッケージ管理
asdf × ×
Poetry ×

Docker (Dev Container) と pip で構築している方も多そうです。
(あとで説明するように Docker (Dev Container) + pip には一部難点があります)

Python のバージョン指定 仮想環境 パッケージ管理
pip × ×
Docker (Dev Container) ×

Rye ですべてを解決するという人も増えてきていると思います。

Python のバージョン指定 仮想環境 パッケージ管理
Rye

Python の開発環境構築の、本当に初歩の初歩としては「pyenv などを使わずになんとなく Python をインストールして、仮想環境なしで pip を使う」ことだと思います。
しかし、ちゃんとした開発環境を構築するためには、上記の 3 つの観点でツールをそろえることになるわけです。

ここから、この 3 つの観点について説明していきます。

Python のバージョン指定

Python のバージョン指定
pyenv
asdf
Docker (Dev Container)
Rye

Python の環境構築で重要な要素のひとつは、Python のバージョン指定です。
たとえば Python の 3.11 を使うのか 3.10 を使うのか指定したい、ということですね。

この用途で有名なツールとしては「pyenv」が挙げられます。

pyenv でも全然問題ないのですが、個人的には「asdf」がおすすめです。
「asdf」は他のプログラミング言語のバージョン指定であったり、kubectl や Terraform、後述する Poetry といったツールのバージョン指定にも使えるためです。

Python の特定バージョンを使いたいという目的は、Docker (Dev Container) を使うことでも果たせます。

また、あとで少し説明する Rye で解決することも可能です。

仮想環境

仮想環境
venv
Docker (Dev Container)
Pipenv
Poetry
Rye

Python の「仮想環境」といえば、仮想マシンやコンテナではなく、venv のようにパッケージのインストール先を変更する技術を指します。

仮想環境なしで pip を使うと、パッケージがグローバルにインストールされるため、他の Python プロジェクトとバージョンの競合を引き起こすことが多いです。
venv などの仮想環境を使うことで、プロジェクトごとにパッケージのインストール先ディレクトリを変更し、バージョンの競合を避けることができます。

venv を使うのは以下のような流れになります。

python -m venv .venv # 仮想環境を作成
. .venv/bin/activate # 仮想環境を有効化
pip install fastapi  # パッケージを仮想環境にインストール
deactivate           # 仮想環境を終了

この使い方で全然問題ないのですが...正直、activate するのを忘れませんか?
activate を忘れたまま pip install するとパッケージはグローバルにインストールされてしまいます。
それだけの問題といえばそれだけなので、activate を忘れなかったり、activate を忘れて pip install しても色々解決できたりするなら、venv を直接使ってもいいと思います。

ただ、Pipenv や Poetry、Rye を使うと、activate 忘れによる問題は発生しません。
Node.js で言うところの npm (npx) や yarn のように、自動的にそのプロジェクト用のディレクトリのパッケージを認識して使わせることができます。

ちなみに、そもそも仮想環境が必要な原因(他のプロジェクトとのパッケージの競合)については、Docker (Dev Container) を使うことでも解決できます。

パッケージ管理

パッケージ管理
pip
Pipenv
Poetry
Rye

最後にパッケージ管理についてです。

Python で最も有名なパッケージ管理ツールは、公式が提供する「pip」です。
Python のパッケージのインストール手順などには pip install <パッケージ> を実行するよう書かれていることが多いです。

ただ、pip install でパッケージをインストールする方法にはいくつかデメリットがあります。
pip install のデメリットはややこしいので、少し長くなりますが丁寧に説明していきます。

前提として、pip install には大きく以下の 2 つの使い方があります。

  • pip install <パッケージ> でインストール => pip freeze > requirements.txt で一覧化
  • インストールしたいパッケージを手作業で requirements.txt に書く => pip install -r requirements.txt でインストール

1 つ目の手順の問題点から説明していきます。

pip install <パッケージ> でインストール => pip freeze > requirements.txt で一覧化」の問題点

Python のパッケージのインストールでは、pip install <パッケージ> というコマンドを使う方法が最も知られていると思います。

しかし、pip install <パッケージ> でインストールしたパッケージは、明示的に pip freeze > requirements.txt のようにして requirements.txt に書き込まない限り、一覧化されません。
明示的に requirements.txt を作成しないと、何のどのバージョンをインストールすればいいのか分からなくなってしまうのです。

pip freeze で requirements.txt を作成すればいいだけでは?と思うかもしれませんが...正直、作り忘れませんか...?
とくに、慣れていない人に忘れずに pip freeze を実行して requirements.txt を作ってもらうのはなかなかハードルが高いと思います。

pip freeze で作成した requirements.txt の欠点

また、pip freeze で requirements.txt を生成する方法では、特定の環境だけで必要なパッケージ(例えば開発中だけ必要なフォーマッタやテスト用のライブラリなど)を、他の環境でも必要なパッケージと分けて一覧化するのがとても大変です。

さらに、pip freeze で生成した requirements.txt から、あるパッケージとその依存関係を適切に削除するのも難しいです。
不要になったパッケージとその依存関係をうまく削除できないのです。

「インストールしたいパッケージを手作業で requirements.txt に書く => pip install -r requirements.txt でインストール」の問題点

ここまでに説明した requirements.txt の問題を回避するには、以下の手順で運用するという方法があります。

  1. インストールしたいパッケージを手作業で requirements.txt に書く
  2. pip install -r requirements.txt でインストールする

このようにすれば、requirements.txt と requirements-dev.txt のように開発中だけ必要なパッケージを分けて管理することができます。
また、不要になったパッケージの削除も簡単です。

しかし、この手順は pip の使い方として普及しきっておらず、慣れていない人に守ってもらうのは難しいと思います。

lock ファイルを作成したい

また、直接 requirements.txt に書いたパッケージ以外にも、その依存先のパッケージまでバージョンを一覧化しておきたいこともあります。
いわゆる lock ファイルを作りたいということですね。

自分の経験からの感覚として、Python のパッケージはかなりしっかりバージョンを固定しないと、動かなくなりやすいです。
例えば FastAPI が依存する先の Pydantic のバージョンまでファイルに書き込んで固定しておかないと、急に動かなくなったりしやすいということです。[2]

pip を使う場合、pip freeze で lock ファイルを作成できます。
つまり、パッケージのインストールは以下のような手順になります。

  1. インストールしたいパッケージを手作業で requirements.txt に書く
  2. pip install -r requirements.txt でインストールする
  3. pip freeze > requirements.lock で lock ファイルを作成する

これで解決ではあるのですが...
この手順は、慣れていない人に守ってもらうのは難しくないでしょうか...?
個人的には、自分一人でコードを書くときも、このような長い手順は避けたいです。

そして、このような pip install の欠点は、Docker (Dev Container) を使っても解決しません。

(ちなみに、上記の 1〜3 の手順はこれでも単純化していて、たとえば venv の仮想環境を使う場合はその activate なども忘れずに必要です)

pip install の苦しさの解決策

このような pip install の欠点は、Pipenv や Poetry、Rye といったパッケージ管理ツールを使えば解決します。
Pipenv・Poetry・Rye では、上記の 1〜3 の手順が単純なコマンド (pipenv install や poetry add、rye add・rye sync) にまとまっています。

pip install の苦しいところを色々書いてきましたが、Pipenv・Poetry・Rye のどれかを使えば解決できるということです。

まとめ

最後に、結局どうすればいいのか、おすすめ構成を 4 パターンまとめます。

pyenv (または asdf) + venv + pip

Python のバージョン指定 仮想環境 パッケージ管理
pip × ×
venv × ×
pyenv (または asdf) × ×

まず、pyenv (or asdf) + venv + pip という構成です。
仮想環境とパッケージ管理が標準のツールだけで完結するため、ツール導入のハードルは低いです。

venv や pip にはここまで書いてきたようなデメリットもありますが、標準のツールで完結させることを優先するならありえる選択肢だと思います。

ただ、私の意見としては、初心者向けではなく、中〜上級者向けの構成だと思います。

この構成については以下の記事も参考になります。

asdf + Poetry (または Pipenv)

Python のバージョン指定 仮想環境 パッケージ管理
asdf × ×
Poetry (または Pipenv) ×

Docker (Dev Container) を使わず PC の環境で直接開発するなら、asdf + Poetry (または Pipenv) という構成がおすすめの 1 つです。

pyenv ではなく asdf をおすすめするのは、Poetry や Pipenv のバージョンの指定にも対応しているためです。

私は簡単に開発環境を作りたいときは、この構成を使うことが多いです。

Docker (Dev Container) + Poetry (または Pipenv)

Python のバージョン指定 仮想環境 パッケージ管理
Docker (Dev Container) ×
Poetry (または Pipenv) ×

次に、Docker (Dev Container) と Poetry (または Pipenv) という組み合わせもおすすめです。
Python のパッケージは OS のパッケージ (apt などでインストールするパッケージ) に依存することが多いので、開発環境をコンテナ化するメリットを享受しやすいです。

Docker で開発環境を構築する場合、近年は Dev Container を使うのが簡単で使い心地も良いです。
Docker (Dev Container) を使うとしても、pip を直接使う問題点は残るため、Poetry や Pipenv の導入は検討してみてください。
(Poetry・Pipenv は Devcontainer Features で Dev Container にとても簡単に導入できます)

Rye

Python のバージョン指定 仮想環境 パッケージ管理
Rye

最後に...
この記事で注目した 3 つの観点を 1 つのツールで解決できるのが「Rye」です。

Rye は README.md に以下のように書かれています。

An Experimental Package Management Solution for Python

Experimental なツールと書かれているので、記事や勉強会などで堂々とおすすめしにくい気持ちになったりはするのですが...使い心地は最高です。

Docker (Dev Container) なしで Python の開発環境を構築する場合は、一番楽に気持ちよく使える環境になると思います。

脚注
  1. 他にも IDE やエディタの選択といった観点があると思いますが、そこは深掘りしません。 ↩︎

  2. 最近 Pydantic の 2.0 のリリースで色々なパッケージが動かなくなりましたが、lock ファイルはそのような問題を防いでくれます。 ↩︎

Discussion