pythonのimport文・パッケージ化について

python3.9 のインポート文の使い方について、まとめる。
これでもう悩まない！
(2022/08/27 更新: 動作確認, 循環参照についての言及を追加, その他記事微修正)

### 同じディレクトリからインポート
同じ階層に `module.py` があるとき、

```python
import module
from module import func_in_module
from module import *
# import module.func_in_module # エラー
```
などとできる。
(funcはクラスでも同じ。以下同様)

### ひとつ下のディレクトリからインポート
同じ階層にディレクトリ `package` があり、その中に `module2.py` があるとき、

```python
import package.module2
from package import module2
from package.module2 import func_in_module2
```
などできる。

### ふたつ以上下のディレクトリからインポート
前項の `package` ディレクトリの中に `subpackage` ディレクトリがあり、その中に `module3.py` があるとき、

```python
import package.subpackage.module3
from package.subpackage import module3
```
ができる(あまり使い所があるとも思えないが)。

### パッケージ化したときの挙動
上述の `package` ディレクトリ、`subpackage` ディレクトリに `__init__.py` という名前のファイル(中身は空で良い)を配置すると、`package` や `subpackage` がパッケージ(モジュールをいい感じにまとめたもの)として認識されるようになる。
このとき、`package/module2.py` においては、

```python
from . import subpackage
from .subpackage import module3
# import subpackage # エラー
# from subpackage import module3 # エラー
# import .subpackage # 以前は使えた?が Python3.8 以降では Syntax error
```
のように `.` を先頭に使った位置関係の指定が使えるようになる(というか、使わないとエラーになるので注意)。
`subpackage` 以下にさらにサブパッケージがある場合は、さらに.でつなげることでインポートできる。

また、例えば `package/subpackage/module3.py` においては、

```python
from .. import module2 
from ..module2 import *
from ..module2 import func_in_module # エラー
# import ..module2 # シンタックスエラー
```
のように、`..` を使って一つ上のディレクトリからインポートすることもできる。
繰り返しだが、これは `__init__.py` を両方のディレクトリに配置してパッケージ化したときの挙動である。

### パッケージ化して外部からimport

前節では `__init__.py` は空としたが、ここに同じルールでインポート文を書くことで再帰的にインポートできる。これによって外部から最上位のモジュールをインポートするだけでの下位モジュールまでアクセスできるようになる。

例えば、

```python:package/__init__.py
import .module2 # package/module2.pyというファイルがある
import .subpackage # package/subpackageというディレクトリがある
```

```python:subpackage/__init__.py
import .module3 # package/subpackage/module3.pyというファイルがある
```

としたうえで `package` ディレクトリに `PythonPath` を通しておけば、

```python:外部スクリプト
import package
print(package.module2.function)
print(package.subpackage.module3.function)
```

のように、`package` を import するだけでアクセスできるようになる。

愚直にコロンをつなげるのが面倒であれば、例えば

```python: package/__init__.py
from .subpackage.module3 import function
```

のように書けば名前が圧縮されて

```python:外部スクリプト
import package
print(package.function)
```

のように参照できるようになる。

余談だが、`__all__` を用いることで再帰的にインポートするモジュールの指定を簡略に行う方法もある: 
[Python公式ドキュメント](https://docs.python.org/ja/3/tutorial/modules.html#importing-from-a-package)

#### ※ 注意 ※
上記のインポート文たちはいずれかひとつを書くぶんには動くが、複数を同時に書くと次のようにエラーになることがある。

> ImportError: cannot import name 'foo' from partially initialized module 'package.module2' (most likely due to a circular import)

これは `module.py` のインポート文を処理しているあいだに `module2.py` で `module.py` をインポートしようとしたために起こるエラー(循環参照, 循環インポート?)である。実務でもしばしば遭遇するので注意。
こうなってしまった場合は、循環参照しないようなモジュール構成を考え直す(インポート関係がDAGになるようにする)か、同しようもない場合は実行時インポートにする(ファイルの先頭に書くのでなく、関数内など実行時に走る部分にインポート文を書く)。


### パッケージ化していないときに上下のディレクトリから
パッケージ化してないけどファイルの置いてある一つ上のディレクトリからインポートしたいときは、

```python

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
import (一つ上のディレクトリにあるpythonファイル名)
```
とすれば良い。
`__file__` でファイルのパスを取得し、その一つ上のディレクトリを動的にモジュール検索対象のパスに加えている。
2行目を

```python
sys.path.append("..")
```
とするのは、プログラムを実行する場所によって結果が変わってしまうため、不適切。

### 結局実用上は
少量のコードであれば、困ったら `sys.path.append()` する。

まとまった量のコーディングをするのであればパッケージ化し、一番上のパッケージをインポートするだけで、中のサブパッケージをインポートするように `__init__.py` に書く。具体的には、各ディレクトリの `__init__.py` に

```python
from .(同じ階層に存在するディレクトリ名) import *
```
をディレクトリの数だけ書く。
これで一番上のディレクトリをパスに追加して

```python
from package import *
```
するだけでその中で定義した全てのクラスが使えるようになる(`*` は説明のために書いてるだけで、実際は適宜行儀の良いインポート文を書くのがよい)。