Pythonランチャーで気軽にPythonの色々なバージョンを試す

## はじめに
たまたまPythonのインストール方法周辺のページを読み直していたところ、Pythonランチャーについての記載を見つけまして（なぜこれまで見落としていたのか自分に問い詰めたいです）、面白そうだなと思ったので使い方と使ってみてどうだったかを記載します。

https://www.python.jp/index.html

## Pythonランチャーとは
ChatGPTに聞いてみたところ以下のように返ってきました。

>Python ランチャー (Python launcher) は、Windows プラットフォーム上で複数の Python インストールを管理し、適切なバージョンの Python インタプリタを実行するためのツールです。Python ランチャーは Python をインストールすると一緒にインストールされ、通常は Python のバージョンと関連付けられた拡張子（.py）の関連付けを行います。

>Python ランチャーには次のような主な機能があります：
>1. **複数バージョンのサポート**: Windows に複数の Python バージョンがインストールされている場合、Python ランチャーは各スクリプトがどのバージョンの Python インタプリタを使用するかを選択するための仕組みを提供します。
>2. **スクリプト指定のバージョン管理**: スクリプト自体の shebang 行 (例: `#!/usr/bin/env python`) で特定の Python バージョンを指定している場合、Python ランチャーはそれに従って適切なバージョンの Python インタプリタを選択します。
>3. **バージョン指定の実行**: コマンドラインから直接 Python バージョンを指定して実行することができます。例えば、`py -3.8 script.py` とすることで Python 3.8 を使用してスクリプトを実行できます。
>4. **環境変数 PYTHONPY**: 環境変数 PYTHONPY を設定することで、デフォルトの Python バージョンや振る舞いをカスタマイズすることができます。
>Python ランチャーは、Windows 上で複数の Python バージョンを使用する際に便利であり、特に開発環境で異なるプロジェクトに異なる Python バージョンが必要な場合に役立ちます。

とのことでした。
実際に試してみた結果、1~3それぞれの機能の動作（4の環境変数は試していないので除く）が確認できました。

## 動作環境
Windows11

## Python 3.12, 3.11, 3.10をそれぞれインストールする
こちらの非公式ダウンローダーを使ってPython3.10から3.12をインストールします。

https://pythonlinks.python.jp/ja/index.html

上記の非公式ダウンローダーを使うと環境変数を設定してもらえる（環境変数を設定するようにチェックする必要あり）ので楽です。

インストールできているか確認します。
```py --list```コマンドを実行するとそれぞれどのバージョンのPythonがインストールできたか確認することができます。

```
PS C:\Users\yuuuka> py --list
 -V:3.12 *        Python 3.12 (64-bit)
 -V:3.11          Python 3.11 (64-bit)
 -V:3.10          Python 3.10 (64-bit)
 ```
### py コマンドを使用して確認

```py -（動かしたいバージョン）```という感じで”-”の後に動かしたいバージョンを指定して実行するとPythonが起動します。

```python
PS C:\Users\yuuuka> py -3.12
Python 3.12.0 (tags/v3.12.0:0fb18b0, Oct  2 2023, 13:03:39) [MSC v.1935 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
```

## バージョンの違いで異なる動きをするコードを実行してみる
Pythonの3.12のアップデートからNameErrorやSyntaxErrorの出力内容が変化しました。
なのでPython3.10～3.12それぞれのバージョンで故意にNameErrorとSyntaxErrorを出力させてみます。

```python:python 3.10の場合
PS C:\Users\yuuuka> py -3.10
Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> sys.version_info
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'sys' is not defined
>>> print(f"{abc def}")
  File "<stdin>", line 1
    (abc def)
         ^^^
SyntaxError: f-string: invalid syntax
>>> 
```

```python:python 3.11の場合
PS C:\Users\yuuuka> py -3.11
Python 3.11.7 (tags/v3.11.7:fa7a6f2, Dec  4 2023, 19:24:49) [MSC v.1937 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> sys.version_info
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'sys' is not defined
>>> print(f"{abc def}")
  File "<stdin>", line 1
    (abc def)
         ^^^
SyntaxError: f-string: invalid syntax
>>> 
```

```python:python 3.12の場合
PS C:\Users\yuuuka> py -3.12
Python 3.12.0 (tags/v3.12.0:0fb18b0, Oct  2 2023, 13:03:39) [MSC v.1935 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> sys.version_info 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'sys' is not defined. Did you forget to import 'sys'?
>>> print(f"{abc def}")
  File "<stdin>", line 1
    print(f"{abc def}")
                 ^^^
SyntaxError: f-string: expecting '=', or '!', or ':', or '}'
>>> 
```

3.10~3.11までは出力されている内容が一緒ですが、3.12で変化していることが確認できますね。


## それぞれのPythonにパッケージをインストールする
それぞれのバージョンのPythonにパッケージをインストールするためには
```py -(動かしたいバージョン) -m pip install ○○```というように指定することができます。
インストール対象のパッケージのバージョンを指定してインストールしたい場合は```py -(動かしたいバージョン) -m pip install ○○==(バージョン)```というように記述すれば指定したバージョンのパッケージのインストールができます。

また、該当バージョンのPythonに対応しているパッケージがどれか調べたいという場合は```py -(動かしたいバージョン) -m pip index versions ○○```と実行すると対応しているバージョンが出力されます。


### インストールしたパッケージを使って動かしてみる
試しにPython 3.11にnumpy 1.26.0を、Python 3.12に最近ベータ版がリリースされたnumpy 2.0.0b1をインストールして動作確認してみます。
動作確認のためにはnumpy 1.26.0と2.0.0b1で異なる挙動を行うコードを書いてみる必要があるので以下の新機能を試してみます。
```A new variable-length string dtype, StringDType and a new numpy.strings namespace with performant ufuncs for string operations```

https://github.com/numpy/numpy/releases/tag/v2.0.0b1


```python:Python3.11&numpy 1.26.0
PS C:\Users\yuuuka> py -3.11              
Python 3.11.7 (tags/v3.11.7:fa7a6f2, Dec  4 2023, 19:24:49) [MSC v.1937 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> print(np.__version__)
1.26.4
>>> from numpy.dtypes import StringDType
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name 'StringDType' from 'numpy.dtypes' (C:\Users\yuuuka\AppData\Local\Programs\Python\Python311\Lib\site-packages\numpy\dtypes.py)
>>>
```

```python:Python3.12&numpy 2.0.0b1
PS C:\Users\yuuuka> py -3.12
Python 3.12.0 (tags/v3.12.0:0fb18b0, Oct  2 2023, 13:03:39) [MSC v.1935 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> print(np.__version__)
2.0.0b1
>>> from numpy.dtypes import StringDType
>>> data = ["this is a longer string", "short string"]
>>> arr = np.array(data, dtype=StringDType())
>>> arr
array(['this is a longer string', 'short string'], dtype=StringDType())
>>>
```

想定通りnumpyバージョン2からの新機能であるStringDTypeが1.26.4では使えないこと、2.0.0b1では使えることが確認できました。

## 使ってみた感想
実務などで使う機会は少ない（というか無さそう）と思いますが、
各バージョンのPythonでの挙動を手軽に少し確認するには良さそうだなと思いました。
各バージョンに合わせたライブラリもそれぞれ入れて試せるというところも良いなと思いました。
ただ、installするライブラリが増えていくと管理が手間になってしまうので、業務で本格的にPython等のバージョンを移行するために動作確認するのであれば、やはりDocker環境を作る方が良いのだろうなと思いました。

## 参考

https://www.python.jp/install/windows/py_launcher.html#mzDjw

Pythonランチャーについて理解の助けになるリンク

https://gammasoft.jp/blog/find-out-python-launcher/

https://python-beginner.blog/whatpyexe/