WindowsでPython環境を（再）構築する

# 初めに
最近，PCを修理し，それまで利用していたPython環境を再構築することになった．

非常に良い機会だと思うので，イチから環境構築する過程を記録しようと思う．

[過去の投稿](https://qiita.com/ShotaDeguchi/items/3df9226685f26aa42d63)では，かなり大雑把に記した気がするので，本稿はもう少し丁寧にウォークスルーして差別化しようと思う．

OSはWindows 10（64-bit）である（Windows 11ユーザーの友人にも試してもらったが，同じ手順が適用できた）．

:::note warn
**注意**
環境構築では[Anaconda](https://www.anaconda.com/)が有名だが，~~著者はAnacondaアンチなので~~ Anacondaは大きすぎて嫌 + condaコマンドが面倒なので[^1]，もう少し軽量で管理が簡単な（だと思う）方法を紹介する．もしAnacondaユーザーが本稿に記載の方法に乗り換える場合は，まず，[Anacondaをアンインストール](https://docs.anaconda.com/free/anaconda/install/uninstall/#:~:text=Search%20for%20%E2%80%9CControl%20Panel%E2%80%9D%20in,Click%20Uninstall.)してから，手順に従っていただきたい．

誤解が無いよう記しておくと，「著者がAnacondaを避け，本稿を記載の方法を選んだ」だけである．この方法を薦める意思は全く無い．環境構築の方法は複数あるから，各ユーザーが自分で選択すれば良い．
:::
[^1]: こう書くと一方的に嫌っているように見えるが，著者もAnacondaを1年以上利用した経験がある．ただ食わず嫌いしているわけでは無い．

# 環境構築
## 仮想環境
本稿は， **仮想環境**を作ることを念頭に置いている．仮想環境を構築することの重要性は，

1. **環境ぶっ壊れ**の心配が少ない（精神衛生上極めて重要）
1. 複数のPythonを切り替えて利用できる
1. 複数のパッケージを切り分けてインストール・利用できる

ことなどにあるだろう．

:::note info
**環境ぶっ壊れ**って？
著者の造語だが，以下のような状況を指す．
一旦苦労して環境を作ったものの，新しいパッケージが必要となり，それをインストールすると既にあった他パッケージとのバッティングが起き，じゃあこっちを消してこっちを入れて，色々やってる内に環境がしっちゃかめっちゃかに，，またイチから環境を作り直そうかな，，（θ~θ;）ﾔﾚﾔﾚ
:::

項目2，項目3については，複数の作業をしているときに，

|Project|A|B|C|
|:-:|:-:|:-:|:-:|
|`Python version`|`=3.12.1`|`<=3.8.10`|`>3.7.2`|

など，複数のPythonを使う必要に迫られる場合に対応するためである（現状はそうでなくても，Pythonを使っていると将来的にそうなる可能性は極めて高い）．また，特定のライブラリが特定のPythonバージョンでしか動かない場合などにも対応できるようになる（機械学習では，PCに搭載しているGPUによってインストールすべき`TensorFlow` / `PyTorch`のバージョンが決まっている場合もある）．

## Python
まず，Pythonが無いことを確認する（既に有るなら，次のpyenv / pyenv-winへジャンプ）．Command Promptで，
```terminal
$ python
```
と打つと，以下のようにMicrosoft Storeへ案内される．もしPythonが有れば，インストールされているPythonが呼び出されるはずなので，このタイミングでは未だPythonが無いことが分かる．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529612/5643821f-7cc5-82a4-01a9-827767fbdd3c.png)

ここで`Get`をクリックしても良いのかもしれないが，

* 私がMicrosoft Storeからインストールしたことが無い
* 私はMicrosoft Storeからインストールする人を見たことが無い
* 公式が安心

という理由から，[Python公式](https://www.python.org/)からインストールする．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529612/c2f00601-7b03-376f-7dd3-4afccab1a723.png)

`Downloads`から，最新版のインストーラをダウンロードするか，`View the full list of downloads`から目的に合わせたバージョンを選択する．

その後，インストーラを起動し，`Add python.exe to PATH`をチェックしてインストールする（2023/12/13現在，`Python 3.12.1`が最新のようだ）．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529612/38184907-8fbd-23de-bb92-76d9b2a1d3ef.png)

インストールできたら，Pythonが有ることを確認する．Command Promptで，
```terminal
$ python
```
と打つと，以下のように`Python`が挨拶してくれる．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529612/9d774b01-270e-c44b-3b1e-7d613e54519b.png)

初めの行で`Python 3.12.1`が動いていることが分かる．また，標準パッケージの`sys`をインポートし，`sys.version`が正しく実行できていることも分かる．一方，サードパーティ製である`numpy`は未だインストールしていないので，当然インポートできない．

## pyenv / pyenv-win
### インストール
Pythonのインストールと動作確認が終わったところで，pyenvをインストールする．

[pyenv](https://github.com/pyenv/pyenv)は，Pythonのバージョンを管理する便利ツールだが，WSL以外のWindowsでは動かない．そのため，Windowsでは[pyenv-win](https://github.com/pyenv-win/pyenv-win)を利用する．

documentationを読めば，インストール法が5つ以上あることが分かる．documentationで最も簡単とされているPowerShellを利用する方法でも良いが，先ほど折角Pythonをインストールしたのだから，pipを使う方法を選択しよう（と言うか，このためにPythonをインストールしたようなものだ）．

[documentation](https://github.com/pyenv-win/pyenv-win/blob/master/docs/installation.md#python-pip)に従い，Command Promptで，
```terminal
$ pip install pyenv-win --target %USERPROFILE%\\.pyenv
```
を実行する．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529612/7d1f9cab-77e4-bd09-bb03-507d6ccff636.png)

インストールできたようだ（pipのアップデートの案内が出ているが，pyenvとは別の話題なのでここでは放っておく）．最後に，[環境変数の設定](https://github.com/pyenv-win/pyenv-win/blob/master/docs/installation.md#add-system-settings)を済ませる（スクリーンショットを取り忘れたが，各々終えていただきたい）．

ここまで来たら，[正常にインストールできたか確認](https://github.com/pyenv-win/pyenv-win#validate-installation)する．Command Promptで，
```terminal
$ pyenv --version
```
と打てば，`pyenv 3.1.1`が返ってくるので，正しくインストールできたらしい．また，
```terminal
$ pyenv
```
で利用可能なコマンドが表示される．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529612/72d5fd2a-ff68-4d8d-893c-bfd1cee91ab6.png)

### Pythonのバージョン管理
[documentation](https://github.com/pyenv-win/pyenv-win)のQuick startやusageの受け売りだが，折角日本語の資料ということで丁寧に記そうと思う．

:::note warn
**注意**
pyenv-winのコマンドが`pyenv ...`となっているため，本稿では以降，pyenv-winのことをpyenvと呼ぶこととする．
:::

まず，最初にインストールしたPythonのバージョンを確認しておく．Command Promptで，
```terminal
$ python -V
```
を実行すると，`Python 3.12.1`が返ってくる．確かに，`3.12.1`をインストールしたのであった（ここは，各々インストールしたバージョンが違うだろうが，最初にインストールしたものが返ってくるはず）．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529612/0f9e72c5-533e-b79e-1f78-f0654f963071.png)

続いて，pyenvにPythonをインストールする（「もうPythonあるやん」って気持ちはぐっと堪えて）．Command Promptで，
```terminal
$ pyenv install -l
```
を実行すれば，pyenvで利用可能なPythonバージョンが列挙される．縦に長すぎるので，
```terminal
$ pyenv install -l | findstr 3.8
```
などとすれば，`Python 3.8.x`たちが表示されてもう少しだけ見やすい．この中から適当なPythonバージョンを選択し，インストールする．例えば，
```terminal
$ pyenv install 3.8.10
```
を実行すれば，`Python 3.8.10`がインストールされる．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529612/7555375a-80f6-eba3-8874-a574a423f95e.png)

このタイミングで，再度
```terminal
$ python
$ python -V
```
などを実行すると，以下のようになる．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529612/fa9e9ca4-40b3-2efe-64a9-adee9b82e8ef.png)

さっきまで使えていたPythonが，，！と慌てる前に，メッセージを読むと，「pyenvで利用するPythonバージョンを指定せよ」とのこと．大人しく従う．
```terminal
$ pyenv global 3.8.10
```
を実行すれば，globalで実行されるPythonが`Python 3.8.10`に固定される．なお，
```terminal
$ pyenv global 3.12.1
```
を実行してもエラーになる．`Python 3.12.1`はPython公式からインストールしたもので，pyenv上にはインストールしていないからである．

試しに2つくらい入れよう．先ほどと同じ要領で`Python 3.7.9`をインストールし，今，pyenvにインストールされているPythonたちを表示する．
```terminal
$ pyenv versions
```
返事は以下の通り．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529612/cab32335-a127-78e8-6e02-a7cb792345f7.png)

インストールした`3.7.9`と`3.8.10`が表示されているが，`global`に設定した`3.8.10`にスターが付けられている．この状態での`python -V`の返事は`Python 3.8.10`であり，`python`を呼ぶと`Python 3.8.10`が起動した．以上より，`global`で呼び出されるPythonが`Python 3.8.10`となっていることが分かる．切り替えたければ，`pyenv global 3.7.9`などとすれば良い．

明示的に確認したいときは，次のようにすれば良い．Pythonの使い方として，[in-command実行](https://docs.python.org/3/using/cmdline.html)が可能なので，例えば以下を実行する．
```terminal
$ python -c "import sys; print(sys.executable)"
```
返事は以下．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529612/bca8e664-2fcc-ca18-83e6-f6cc5c6991d0.png)

これより，`pyenv`由来の`Python`が呼び出されていることが分かる．

続いて，`pyenv local`の使い方を記す（今の状態は，`pyenv`に`3.7.9`と`3.8.10`があり，`global==3.8.10`である）．`local`の設定をしていないタイミングでは，
```terminal
$ pyenv local
```
を実行しても，

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529612/0c6e900e-4db2-bfcf-ce46-9bd6177b9e66.png)

という返事となる．メッセージは，「このディレクトリには`local`の設定が無い」と言っているので，適当な作業ディレクトリに移動してから
```terminal
$ pyenv local 3.7.9
```
としてみる．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529612/70ca90c9-098b-653b-8f6c-851d0de095aa.png)

（緑色のマスクは作業ディレクトリである）

`global==3.8.10`のまま，`local==3.7.9`となり，このディレクトリでは`Python 3.7.9`が呼ばれている．因みに，`pyenv local 3.7.9`を実行したこの作業ディレクトリには`.python-version`というファイルが作られ，その中には`3.7.9`とだけ書いてあった．

また，

* この作業ディレクトリより上では`python -V`の返事は`3.8.10`
* この作業ディレクトリより下では`python -V`の返事は`3.7.9`

となっており，`local`を設定した階層（`.python-version`がある階層）以下では全て`local`を設定した階層の（つまり，`.python-version`に記された）バージョンが使われることが分かる．

:::note info
**pyenvの利用**
pyenvの利用により，複数のPythonバージョンが利用・管理できるようになる．
:::

## venv
### 仮想環境の構築とアクティベート
pyenvが終われば，もう全て終わったようなものである．

先に記した仮想環境の構築のためのツールがvenvな訳だが，これは`Python 3.3`以降の`Python 3.x`には標準で搭載されている．つまり，pyenvで`3.3`以降のPythonを入れておけば殆ど準備が済んでいる．

仮想環境の運用として，globalな仮想環境というのは違和感がある．どちらかというと，プロジェクト毎に違う環境が要求されるだろう．先ほどの作業ディレクトリを，あるプロジェクトの作業場とし，そこに仮想環境を作ることにする．

[公式](https://docs.python.org/3/tutorial/venv.html)に従い，以下のコマンドで`.venv_test`という名前の仮想環境を作る．
```terminal
$ python -m venv .venv_test
```
`.venv_test`という名前のディレクトリができる．これが（未だ空っぽの）仮想環境である．この仮想環境をアクティベートする．
```terminal
$ . .venv_test/Scripts/activate
```
ただし，上記を実行すると，`.venv_test/Scripts/Activate.ps1 cannot be loaded because running scripts is disabled on this system.`などと表示され，アクティベートできない場合がある．これは，PowerShellで起きる場合が多いようだ（例えば，VSCodeのターミナルはデフォルトでPowerShellであり，これが起きることがある）．原因は，Microsoftの実行ポリシー（[en](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.4) / [jp](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.4)）にある．この解説は，[こちらの記事](https://zenn.dev/ricmsd/articles/7f2ceade6581db)が詳しい．

原因の完全な理解は置いておいて，取り敢えず，解決に注力する．[公式](https://docs.python.org/ja/3/library/venv.html#module-venv)もこの問題は認知しているようで，以下をPowerShellで実行すれば良い，としている．
```terminal
$ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
```
これをPowerShellで実行すると，「本当に良いか？」との警告が出るので，Yesと回答する．すると，先に記した仮想環境のアクティベートができるようになる．

仮想環境がアクティベートされている間，ターミナルは，
```terminal
(.venv_test) $ 
```
などという待機状態になるだろう．ディアクティベートするには，
```terminal
(.venv_test) $ deactivate
```
とすれば，仮想環境名の表示が消え，
```terminal
$ 
```
という待機状態に戻るだろう．これは仮想環境が立ち上がっていない状態を示している．つまり，きちんとディアクティベートできた，という意味である．必要に応じて，また別の環境をアクティベートすれば良い．

なお，`.venv_test`のPythonは，`Python 3.7.9`になっている（`.venv_test`をアクティベートした状態で`python -V`を実行すれば，`3.7.9`が返ってくる）．なぜなら，このディレクトリでは`local==3.7.9`の設定をしたからである．このとき，`local==3.7.9`が設定されているこのディレクトリより上の階層で仮想環境を作成し，バージョン確認すると，`3.8.10`が返ってくる．なぜなら，今は`global==3.8.10`の設定をしているからである．

このように，作業ディレクトリ毎に仮想環境を作るだけでなく，仮想環境毎に使うPythonバージョンの切り替えまで可能になる．また，他のPythonバージョンを使いたいならば，適宜`pyenv install 3.x.x`などとしてから，`global` / `local`の設定を済ませ，`python -m venv .venv_test`で仮想環境を作れば良い．

### 仮想環境へのライブラリのインストール
めでたく仮想環境が構築できたわけだが，先に記したように，これは未だ空っぽである．つまり，標準ライブラリしか利用できない（数値計算も，機械学習も，グラフ描画もできない）．

試しに，

```python: np_plt_test.py
import numpy as np
import matplotlib.pyplot as plt

def main():
    x = np.linspace(0., 2. * np.pi, 100)
    y = np.sin(x)

    plt.plot(x, y)
    plt.show()

if __name__ == "__main__":
    main()
```
というスクリプトを実行しても，`No module named 'numpy'`などと怒られる．

例えば，当該の仮想環境（`.venv_test`とする）で`numpy`や`matplotlib`を使いたいならば，当該環境をアクティベートした状態で，
```terminal
(.venv_test) $ pip install numpy, matplotlib
```
してから再度，先ほどのスクリプトを実行すれば良い．上記のコマンドでは，当該のPythonに対応する中で最新バージョンのライブラリがインストールされる．あるいは，特定のバージョンが欲しいときには，
```terminal
(.venv_test) $ pip install numpy==1.24.4
```
などとすれば良い．

一旦`numpy`をインストールしたが，間違ったバージョンを入れてしまった！という場合には，
```terminal
(.venv_test) $ pip uninstall numpy
```
してから，再度，バージョンに気を付けながらインストールする．

また，開発環境を誰かと共有する場合や，過去に作った仮想環境に類似した環境を必要とする場合には，
```terminal
(.venv_test) $ pip freeze > requirements.txt
```
を実行する．すると，同じ階層に`requirements.txt`というファイルが作成される．同ファイルには，`.venv_test`で使われているライブラリのバージョンが`numpy==1.24.4`という形式で記されている．

新しい環境（`.venv_test2`とする）で同じ環境を作るには（勿論，`.venv_test`をディアクティベートしてから，`.venv_test2`をアクティベートした状態で），
```terminal
(.venv_test2) $ pip install -r requirements.txt
```
を実行する．これで，名前が違うが全く同じ仮想環境ができる．ここから，`(.venv_test2)`にだけ新しいライブラリを入れてみたり，幾つかのライブラリのバージョンを変えてみたり，ということが可能となる．

最後に，仮想環境を間違って作ってしまった場合や，もう使わないな，という場合は，`.venv_test`をディレクトリごと削除すれば良い．
```terminal
$ rm -r .venv_test
```
先に記した**環境ぶっ壊れ**が起きたとしても，それは当該の仮想環境だけの話だから，被害は最小限に留めることができる．この意味合いで安全である（他の仮想環境はぶっ壊れない）．

このようにして，目的に応じて仮想環境を切り分ければ，お互いのバージョン互換性などに悩まされることが少なくなる．

:::note info
**venvの利用**
venvの利用により，仮想環境の構築，利用が簡便になる．
:::

# 終わりに
久しぶりに，Pythonの環境を作るという作業をした．大きな問題もなく環境整備ができて良かった．また，その過程を本稿に記録することができて良かった．

特に，pyenvの使い方は良く理解していなかったので，発見があり，楽しかった．

ゆっくり書きすぎた気もするが，丁寧で損することは無いだろう．