Pythonのimportについてわかった気になれる記事

Pythonのimportについて、全然理解できていなかったので、自分なりにまとめてみました。

もし、間違いがありましたら、指摘していただけると嬉しいです！

## 動作環境

```
$ python3
Python 3.7.1 (default, Nov 17 2018, 02:39:57)
[Clang 10.0.0 (clang-1000.10.44.4)] on darwin
```

## サンプルソース

一応、https://github.com/tamago324/python-import-sample/tree/master/package においてあります

```
$ tree
.
└── package
    ├── __init__.py
    ├── module_a.py
    ├── sub_package_b
    │   ├── __init__.py
    │   └── module_b.py
    │── sub_package_c
    │   ├── __init__.py
    │   └── module_c.py
    └── sub_package_d
        ├── __init__.py
        └── module_c.py
```

## モジュールとパッケージ

まずは、モジュールとパッケージについて

モジュールは、Pythonのファイル(`*.py`)

パッケージは、モジュールを一つのディレクトリにまとめたもの(`./package/`)。また、パッケージ内にさらにパッケージを含めることもできる(`./package/sub_package_b/`)

たぶん、こんな感じだと思います。

## `__init__.py`

importしたときに1度だけ実行されるファイル  
もし、パッケージの下にさらにパッケージがある場合、`__init__.py`でimportしておくべき

`__init__.py`はサブモジュールをimportする役割があるのは、初めて知った。パッケージにはとりあえず**空の**`__init__.py`をおくっていう認識だったから...

## 絶対import

パッケージ・モジュールは`sys.path`のディレクトリから検索される

```python
$ python3
>>> import package
>>> import package.sub_package_b
>>> from package.sub_package_c import module_c
```

もし、import対象のモジュール・パッケージが`sys.path`のディレクトリにない場合、`ModuleNotFoundError`が発生する

## 相対import

`from`の後に`.`を付けた特別な`import`のことを相対importという  
また、相対importは「パッケージ内」でのみ使える

```python:package/__init__.py
from . import module_a
from . import sub_package_b
from .sub_package_b import module_b
```

### サブモジュールから別のサブモジュールをimport

相対importを使うことで、サブモジュールから別のサブモジュールをimportすることができる

例) `package.sub_package_b`から`package.sub_package_c`をimportする場合

```python:package/sub_package_b/__init__.py
from .. import module_a
from .. import sub_package_c
```

`..`のように、上の階層に行くことができる

### 芋づる式にimportされている

連鎖的にimportすることもできる

```python:package/__init__.py
from . import module_a
from . import sub_package_b
from .sub_package_b import module_b     # sub_package_bをimport
```

```python:package/sub_package_b/__init__.py
from .. import module_a
from .. import sub_package_c            # sub_package_cをimport
```

- `package/__init__.py`で`package.sub_package_b`をimport
- `package/sub_package_b/__init__.py`で`package.sub_package_c`をimport

となっているため、`package`をimportすれば、`package.sub_package_c`を呼び出すことができる

```python
$ python3
>>> import package
>>> package.sub_package_b
<module 'package.sub_package_b' from '/Users/tamago324/python-import-sample/package/sub_package_b/__init__.py'>

>>> package.sub_package_c
<module 'package.sub_package_c' from '/Users/tamago324/python-import-sample/package/sub_package_c/__init__.py'>

```

逆に、`package.sub_package_d`はどこからもimportされていないため、明示的にimportしないと呼び出せない

```
>>> package.sub_package_d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'package' has no attribute 'sub_package_d'
```

## `if __name__ == "__main__":`

よくPythonのプログラムで見る`if __name__ == "__main__":`について。

モジュールが直接実行された場合、`__name__`には`__main__`が格納されるため、このような記述になっているということ


```python:package/name_test.py
print(f"__name__: {__name__}")
```

```python
$ pwd
/Users/tamago324/python-import-sample/

$ python3 -m package.name_test
__name__: __main__
```

モジュールが直接実行された場合、たしかに`__name__`には`__main__`が入っていることが確認できた

`-m`オプションを付けることでパッケージ内で相対importができるようになる(らしい)


もし、間違いがありましたら、　指摘していただけると嬉しいです！

## 参考文献

- [Python の import 文ってなに？ | Mastering Python](https://python.ms/import/)
- [PEP 328 - 複数行の importそして 絶対 import と相対 import | Mastering Python](https://python.ms/import/pep328/)
- [Python 3でのファイルのimportのしかたまとめ - Qiita](https://qiita.com/karadaharu/items/37403e6e82ae4417d1b3)
- [Python基礎講座(14 モジュールとパッケージ) - Qiita](https://qiita.com/Usek/items/86edfa0835292c80fff5)