第4回 Python仮想環境(venv)を仕組みから理解する 〜なぜ仮想環境が必要なのか〜
対象読者
- Homebrew版Pythonを導入した方
- Pythonの仮想環境(venv)を初めて使う方
python3 -m venv .venvの意味を理解したい方- 研究や開発でPython環境を整理したい方
AI利用について
✍️ 本記事は 生成AI(ChatGPT)によって作成 されました。
誤りがあればコメント等でご指摘いただけると助かります。なお、本記事に含まれる内容については、執筆時点での情報に基づいています。
環境やソフトウェアのバージョンによって挙動が異なる場合があるため、必要に応じて公式ドキュメントもあわせて参照してください。
はじめに
前回は、Homebrew版Pythonを導入し、Apple標準Pythonとの違いや、PATHの仕組みについて解説しました。
これでMac上で開発用のPythonを利用する準備は整いました。
しかし、ここで新たな問題が生まれます。
例えば、NumPyをインストールしたい場合、多くの人は次のように実行します。
python3 -m pip install numpy
もちろん、この方法でもNumPyはインストールできます。
しかし、このコマンドをそのまま使い続けると、Mac全体で利用するPython環境へNumPyが追加されることになります。
最初のうちは問題ありません。
しかし、研究や開発を続けていくと、複数のプロジェクトを同時に扱うようになります。
例えば、
- 機械学習の勉強
- 大学の課題
- 趣味で作るツール
など、それぞれ異なるライブラリやバージョンを必要とすることがあります。
そのような状況で、すべてのプロジェクトが同じPython環境を共有してしまうと、ライブラリ同士が衝突し、思わぬトラブルの原因となります。
そこで登場するのが、
Python仮想環境(Virtual Environment)
です。
この記事では、単に仮想環境を作る方法を紹介するのではなく、
「仮想環境とは何か」「なぜ必要なのか」「内部で何が起きているのか」
という仕組みまで理解することを目標に解説していきます。
なぜ仮想環境が必要なのか
まずは、仮想環境が必要になる理由を考えてみましょう。
例えば、研究Aでは
NumPy 2.2
を利用しているとします。
一方で、研究Bでは古いプログラムとの互換性のために
NumPy 1.26
が必要だったとします。
もし両方とも同じPython環境を利用している場合、
Python
├── numpy 2.2
├── scipy
├── matplotlib
という状態から、
研究Bのために
python3 -m pip install numpy==1.26
を実行すると、
Python
├── numpy 1.26
├── scipy
├── matplotlib
となり、NumPyのバージョンが上書きされてしまいます。
すると、今度は研究Aが動かなくなる可能性があります。
つまり、
一つのPython環境を複数のプロジェクトで共有することには、大きなリスクがある
のです。
仮想環境という考え方
この問題を解決するために用意されているのが、Python仮想環境(venv)です。
仮想環境を利用すると、それぞれのプロジェクト専用のライブラリ環境を作ることができます。
イメージすると、次のようになります。
Homebrew版Python
│
────────────────┼────────────────
│
┌─────────────┴─────────────┐
│ │
Project A Project B
│ │
.venv .venv
│ │
numpy 2.2 numpy 1.26
matplotlib scipy
ここで重要なのは、
Python本体は共有し、ライブラリだけをプロジェクトごとに分離している
という点です。
つまり、Pythonそのものを毎回コピーしているわけではありません。
Python本体はHomebrew版Pythonを利用しながら、
ライブラリだけをプロジェクトごとに独立して管理します。
これが、Python仮想環境の基本的な考え方です。
実際に仮想環境を作成してみる
それでは、実際に仮想環境を作成してみましょう。
まずは、作業用ディレクトリを用意します。
mkdir ~/Developer/python-learning
cd ~/Developer/python-learning
続いて、
python3 -m venv .venv
を実行します。
一見すると一瞬で終わるため、
「何も起きていないのでは?」
と思うかもしれません。
しかし、実際にはプロジェクト内に新しいディレクトリが作成されています。
まずは通常の ls を実行してみます。
ls
すると、何も表示されません。
これは、
.(ドット)から始まるファイルやディレクトリは、デフォルトでは表示されない
ためです。
そこで、
ls -a
を実行してみます。
すると、
.
..
.venv
のように表示されます。
つまり、
仮想環境は
.venvという隠しディレクトリとして作成される
ことが分かります。
ここからが本記事の本題です。
次は、この .venv ディレクトリの中身を詳しく見ていきます。
.venv の中身を確認してみる
それでは、実際に .venv の中身を確認してみましょう。
まず、
ls .venv
を実行します。
私の環境では、次のように表示されました。
bin
include
lib
pyvenv.cfg
つまり、.venv の中には、
.venv/
├── bin/
├── include/
├── lib/
└── pyvenv.cfg
という構造が作られています。
一見すると単純ですが、それぞれ重要な役割を持っています。
ここからは、
pyvenv.cfgbinlibinclude
の順番に見ていきます。
pyvenv.cfg とは何か
まず、
pyvenv.cfg
から確認します。
cat .venv/pyvenv.cfg
を実行すると、私の環境では次のように表示されました。
home = /opt/homebrew/opt/python@3.14/bin
include-system-site-packages = false
version = 3.14.6
executable = /opt/homebrew/Cellar/python@3.14/3.14.6/Frameworks/Python.framework/Versions/3.14/bin/python3.14
command = /opt/homebrew/opt/python@3.14/bin/python3.14 -m venv /Users/yhiraiwa/Developer/python-lerning/.venv
いきなり見ると、少し難しそうです。
しかし、順番に見ていけば、それぞれ何を意味しているのか分かります。
home について
まず、
home = /opt/homebrew/opt/python@3.14/bin
です。
これは、仮想環境の作成に使用したPython環境の場所を示しています。
今回の環境では、HomebrewによってPython 3.14がインストールされています。
以前確認したように、
which python3
を実行すると、
/opt/homebrew/bin/python3
となっていました。
そして、
ls -l /opt/homebrew/bin/python3
を実行すると、
/opt/homebrew/bin/python3 -> ../Cellar/python@3.14/3.14.6/bin/python3
のようなシンボリックリンクになっています。
つまり、
/opt/homebrew/bin/python3
│
│ シンボリックリンク
▼
/opt/homebrew/Cellar/python@3.14/...
│
▼
Python本体
という構造です。
pyvenv.cfg の
home = /opt/homebrew/opt/python@3.14/bin
も、このHomebrew版Pythonを基準に仮想環境が作成されたことを示しています。
include-system-site-packages について
次に、
include-system-site-packages = false
です。
これは、
システム側のPythonにインストールされているパッケージを、仮想環境から利用するか
を指定しています。
今回の設定は、
false
なので、
システム側のPythonパッケージを仮想環境に含めない
という設定になっています。
これは仮想環境の重要な特徴です。
仮想環境の目的は、
プロジェクトごとにPythonパッケージを分離すること
だからです。
例えば、仮想環境の外側に
numpy
matplotlib
scipy
がインストールされていたとしても、通常はそれらをそのまま利用するのではなく、現在のプロジェクトの仮想環境に必要なパッケージをインストールします。
イメージとしては、
Mac全体
│
├── Homebrew Python
│
│
├── プロジェクトA
│ └── .venv
│ ├── numpy
│ └── matplotlib
│
└── プロジェクトB
└── .venv
├── numpy
└── scipy
というように、それぞれのプロジェクトを分離できます。
これによって、
プロジェクトAのnumpy
と
プロジェクトBのnumpy
を別々のバージョンで管理することも可能になります。
version について
次に、
version = 3.14.6
です。
これは、その仮想環境を作成したPythonのバージョンを示しています。
今回の場合は、
Python 3.14.6
です。
これは、仮想環境を作成する前に確認した、
python3 --version
の結果と対応しています。
つまり、
Homebrew Python 3.14.6
│
│
▼
python3 -m venv .venv
│
▼
.venv
│
└── version = 3.14.6
という関係になっています。
executable について
続いて、
executable = /opt/homebrew/Cellar/python@3.14/3.14.6/Frameworks/Python.framework/Versions/3.14/bin/python3.14
です。
これは、
仮想環境を作成するときに使用されたPython実行ファイル
を示しています。
ここで、
/opt/homebrew/Cellar/
というディレクトリが出てきました。
Homebrewでは、インストールされたソフトウェアの実体が、
/opt/homebrew/Cellar/
以下で管理されています。
一方、
/opt/homebrew/bin/
には、ユーザーがコマンドとして実行するためのシンボリックリンクが配置されます。
そのため、
/opt/homebrew/bin/python3
と、
/opt/homebrew/Cellar/python@3.14/...
は別の場所ですが、リンクによってつながっています。
command について
最後に、
command = /opt/homebrew/opt/python@3.14/bin/python3.14 -m venv /Users/yhiraiwa/Developer/python-lerning/.venv
です。
これは、
この仮想環境を作成する際に実行されたコマンド
を示しています。
今回、自分で実行したコマンドは、
python3 -m venv .venv
でした。
これを具体的なPythonのパスに置き換えると、
/opt/homebrew/opt/python@3.14/bin/python3.14 -m venv /Users/yhiraiwa/Developer/python-lerning/.venv
となります。
つまり、
Python
│
├── -m
│
└── venv
│
└── .venvを作成
という処理を行ったわけです。
python3 -m venv .venv をもう一度理解する
ここで、仮想環境を作成するときに使用した、
python3 -m venv .venv
というコマンドを改めて見てみましょう。
このコマンドは4つの部分に分けて考えると分かりやすくなります。
python3
-m
venv
.venv
それぞれ、
python3
はPythonを実行することを意味します。
-m
は、Pythonのモジュールを実行するためのオプションです。
venv
は、Pythonに標準で用意されている仮想環境作成用のモジュールです。
そして、
.venv
は、作成する仮想環境のディレクトリ名です。
したがって、
python3 -m venv .venv
とは、
Pythonを使って標準ライブラリの
venvモジュールを実行し、現在のディレクトリに.venvという仮想環境を作成する
という意味になります。
これは非常に重要なので、ぜひ覚えておきましょう。
bin ディレクトリ
次に、
bin
を確認します。
ls .venv/bin
を実行します。
私の環境では、
Activate.ps1
activate
activate.csh
activate.fish
pip
pip3
pip3.14
python
python3
python3.14
などが表示されました。
ここには、
- Python
- pip
- 仮想環境を有効化するためのスクリプト
などが入っています。
特に重要なのが、
python
python3
pip
activate
です。
.venv/bin/python3 について
では、
ls -l .venv/bin/python3
を実行してみましょう。
すると、
.venv/bin/python3 -> ...
のような表示になります。
ここで重要なのが、
->
です。
これはシンボリックリンクを示しています。
つまり、
.venv/bin/python3
が、Pythonの実体そのものではなく、
別のPython実行ファイルを指している
ことを意味します。
概念的には、
.venv/bin/python3
│
│ シンボリックリンク
▼
HomebrewでインストールされたPython
という関係です。
ここから、
venvはPythonを丸ごとコピーしているわけではない
ということが分かります。
lib ディレクトリ
次に、
lib
を見てみましょう。
ls .venv/lib
すると、Pythonのバージョンに対応したディレクトリが存在します。
今回のPythonは3.14なので、
python3.14
というディレクトリがあります。
さらに、
ls .venv/lib/python3.14
とすると、
site-packages
というディレクトリがあります。
この、
site-packages
が非常に重要です。
site-packages とは何か
Pythonの外部パッケージは、基本的にこの
site-packages
にインストールされます。
例えば、仮想環境を有効化した状態で、
python3 -m pip install numpy
を実行すると、
.venv/lib/python3.14/site-packages/
の中にNumPyがインストールされます。
同様に、
python3 -m pip install matplotlib
を実行すれば、Matplotlibもこの場所にインストールされます。
つまり、
.venv/
└── lib/
└── python3.14/
└── site-packages/
├── numpy/
├── matplotlib/
└── ...
という構造になります。
これが、
プロジェクトごとにPythonパッケージを分離する
仕組みの中心です。
include ディレクトリ
最後に、
include
です。
これは、PythonのC拡張などをコンパイルするときに必要となるヘッダファイルなどに関連するディレクトリです。
Pythonのパッケージには、Pythonだけで実装されているものだけではありません。
例えば、
- C
- C++
- Fortran
などで実装されたコードを内部で利用するパッケージもあります。
特にNumPyのような数値計算ライブラリでは、Pythonだけではなく、コンパイルされたコードが重要な役割を果たします。
そのため、仮想環境には、
include
というディレクトリも用意されています。
ただし、通常のPythonプログラミングでは、このディレクトリを直接操作することはほとんどありません。
ここでは、
C/C++などと連携する際に利用される場所
くらいに理解しておけば十分です。
.venv 全体を整理する
ここまでの内容をまとめると、.venv は次のような構造になっています。
.venv/
│
├── bin/
│ ├── python
│ ├── python3
│ ├── pip
│ └── activate
│
├── include/
│
├── lib/
│ └── python3.14/
│ └── site-packages/
│ ├── numpy
│ ├── matplotlib
│ └── ...
│
└── pyvenv.cfg
それぞれの役割を表にすると、
| ファイル・ディレクトリ | 役割 |
|---|---|
bin/ |
Python、pip、activateなどの実行用ファイル・スクリプト |
lib/ |
Pythonライブラリを格納する場所 |
site-packages/ |
pipでインストールしたパッケージが入る場所 |
include/ |
C/C++などとの連携に必要なヘッダ関連 |
pyvenv.cfg |
仮想環境の設定情報 |
となります。
ここまで理解できれば、
python3 -m venv .venv
によって「何が作られているのか」がかなり見えるようになったと思います。
仮想環境を作っただけでは、まだ切り替わっていない
ここで、非常に重要な点があります。
python3 -m venv .venv
を実行したからといって、
その瞬間から仮想環境のPythonが使われるわけではありません。
仮想環境を作ることと、仮想環境を利用することは別だからです。
例えば、仮想環境を作成した直後に、
which python3
を実行してみます。
まだ仮想環境を有効化していなければ、
/opt/homebrew/bin/python3
のように表示されます。
つまり、
python3
│
▼
/opt/homebrew/bin/python3
となっています。
まだ、
.venv/bin/python3
には切り替わっていません。
「作る」と「有効化する」は別
ここは今後のPython環境を理解するうえで非常に重要なので、もう一度整理します。
仮想環境を作るコマンドは、
python3 -m venv .venv
です。
一方、仮想環境を有効化するコマンドは、
source .venv/bin/activate
です。
つまり、
① 仮想環境を作成
python3 -m venv .venv
│
▼
.venv/
と、
② 仮想環境を有効化
source .venv/bin/activate
│
▼
現在のシェルが .venv を使うようになる
は、別々の操作です。
今回の重要ポイント
今回、一番覚えておいてほしいことは次の3つです。
① .venv は単なる空のフォルダではない
中には、
bin
include
lib
pyvenv.cfg
があり、それぞれ役割があります。
② Pythonパッケージは site-packages に入る
例えば、
python3 -m pip install numpy
を仮想環境内で実行すると、
.venv/lib/python3.14/site-packages/
にNumPyがインストールされます。
③ 仮想環境を作ることと有効化することは別
python3 -m venv .venv
は、
仮想環境を作る
コマンドです。
source .venv/bin/activate
は、
仮想環境を現在のシェルで使う
ためのコマンドです。
次回予告
ここまでで、
「仮想環境という箱がどのように作られているのか」
が分かりました。
しかし、まだ一つ大きな疑問が残っています。
なぜ、
source .venv/bin/activate
と入力するだけで、
which python3
の結果が
/opt/homebrew/bin/python3
から
/Users/yhiraiwa/Developer/python-lerning/.venv/bin/python3
へ変わるのでしょうか?
そして、ターミナルの先頭に、
(.venv)
と表示されるのはなぜでしょうか?
次回は、この疑問を解決します。
次回のテーマは、
「activateの正体」
です。
source .venv/bin/activate
という一行の裏側で、
-
sourceは何をしているのか -
activateは何をしているのか -
PATHはどのように変更されるのか -
VIRTUAL_ENVとは何か -
(.venv)が表示される理由 -
deactivateは何をしているのか -
hash -rは何のためにあるのか
を、実際の activate スクリプトを読みながら詳しく見ていきます。
「activateすればPythonが切り替わる」という暗記から一歩進んで、
「なぜ切り替わるのか」
を理解することが次回の目標です。