Pythonにおける環境構築周りについて

### `setup.py`とは
Pythonのプログラム一式をパッケージとしてまとめるためのスクリプト。
これにより、Pythonをインストールする際に標準で付属するpipを使ってコードを他人と共有できるようになる。

setup()関数を呼び出す処理を記載して、その引数でパッケージ名やバージョン情報、パッケージに含めるPythonモジュールなどの情報を渡す。

- setup.pyの基本的な構文
```python
from setuptools import setup
setup(
  name='sample',
  version='1.0.0',
  packages=['sample'],
  install_requires=_requires_from_file('requirements.txt'),
)
```
ここで、setup関数の各引数の意味は次の通り。

- `name`: パッケージ名
- `version`: パッケージバージョン
- `packages`: パッケージとなるディレクトリを指定する
  - find_packagesで __ init__.pyが存在するディレクトリを検索して自動でパッケージ指定する。
  - パッケージから外す内容を指定も可能 find_packages(exclude=["*.pyc"])
- `install_requires`: 依存パッケージを指定(requirements.txtで指定)
  - requirements.txtじゃなくても、直接指定も可能
  ```python
  install_requires=[
    'numpy>=1.18.5',
    'flask<=1.1.2',
    'requests==2.24.0',
  ]
  ```

setup.pyは、パッケージのルートディレクトリに置くのが一般的。
例えば、以下のようなディレクトリ構成を持つパッケージ my_package の場合、setup.pyは my_package ディレクトリと同じ階層に置かれる。
```
my_package/
├── my_package/ # パッケージと同じ名前のフォルダを作成する
│   ├── __init__.py
│   └── module1.py
├── tests/
│   ├── __init__.py
│   └── test_module1.py
├── README.md
├── LICENSE
├── setup.py # ルートディレクトリ直下にsetup.pyを置く
└── requirements.txt
```

### `__init__.py`とは [→参考](https://ya6mablog.com/how-to-use-init-py/)
`__init__.py`とは、Pythonパッケージを定義するために必要なファイルの事。ここでの「パッケージ」とは、複数のPythonモジュール（`.py`ファイル）を含むディレクトリ（フォルダ）のことを指す。
下記に`__init__.py`ファイルの役割を簡潔に説明する。

1. **パッケージとしての識別**:
   - `__init__.py`ファイルは、そのディレクトリが単なるフォルダではなく、PythonのパッケージであることをPythonインタプリタに伝える役割を持つ。
   このファイルが存在する場合、Pythonはそのディレクトリをパッケージとして扱い、importを使って外部から呼び出す事ができる。
   このファイルがない場合、ディレクトリ内のモジュールは通常のパッケージとして認識されない。

2. **パッケージの初期化**:
   - `__init__.py`ファイルは、パッケージがインポートされた際に実行されるため、このファイル内で、パッケージの初期設定や、特定のモジュールの自動インポートなどを行うことができる。
   例えば、パッケージ内の複数のモジュールから特定の関数やクラスをインポートし、パッケージレベルで直接アクセス可能にする設定が可能である。

3. **名前空間の管理**:
   - `__init__.py`を使用して、パッケージ内のモジュールやサブパッケージの名前空間（スコープ）を管理する。これにより、パッケージ外部からのインポート時に、どのモジュールやオブジェクトが利用可能かを制御できる。

4. **互換性と整理**:
   - 古いバージョンのPythonでは`__init__.py`が必須であったが、Python 3.3以降では、暗黙の名前空間パッケージという機能により、このファイルがなくてもディレクトリをパッケージとして扱うことができる。しかし、明示的な`__init__.py`を使用することで、コードの整理や互換性の維持に役立つ。
   というか、無いと読込み遅くなったりするので間違いなく作っておく方が良い。

要するに、`__init__.py`ファイルは、Pythonパッケージが正しく機能するための「初期設定スクリプト」として機能する。これにより、パッケージ内のモジュールやサブパッケージを整理し、外部からのアクセス方法を制御することができる。
ちなみに、中身は空でも問題なく読み込める。

### `requirements.txt`とは
Pythonプロジェクトにおいて、そのプロジェクトで必要とされる外部パッケージやライブラリの一覧を記述するファイル。

1. **依存関係の明示**:
   - `requirements.txt`は、プロジェクトが依存するPythonパッケージの一覧を明示する。これにより、他の開発者がそのプロジェクトを手に取ったときに、必要なすべての依存パッケージを容易にインストールできるようになる。

2. **環境の一貫性の確保**:
   - プロジェクトを異なる環境で実行する場合、同じバージョンの依存パッケージが必要となることが多い。`requirements.txt`を使用することで、異なる開発環境間での一貫性を保ちやすくなる。

3. **簡単なインストール**:
   - `pip`というPythonのパッケージ管理ツールを使用して、`requirements.txt`にリストされているパッケージを一括でインストールできる。
   - インストール時のコマンドは以下。
   `pip install -r requirements.txt`
   このコマンドにより、ファイルに記載されたすべての依存パッケージが自動的にインストールされる。

4. **バージョン管理**:
   - `requirements.txt`では、パッケージの具体的なバージョンを指定することができる。これにより、プロジェクトが特定のバージョンのパッケージに依存している場合、その正確なバージョンをインストールできる。

例として、`requirements.txt`の内容は以下のように記述する。

```
flask==1.1.2
requests==2.24.0
numpy>=1.18.5
```

この例では、`flask`と`requests`は特定のバージョンが指定されており、`numpy`は指定されたバージョンまたはそれ以上のバージョンが要求されている。

### `pip freeze`とは
現在の環境にインストールされたパッケージとバージョンがpip install -rで使える設定ファイルの形式で出力されるコマンド。

したがって、`pip freeze > requirements.txt`を実行すると、出力された`requirements.txt`を使って元の環境と同じバージョンのパッケージを別環境に一括でインストールできる。