pythonのパッケージ階層の話

# Overview

ふわっとしたパッケージ構造の理解を説明する試み。
**python 3.7.2** および **python 2.7.15** の場合です。

# パッケージ階層

pythonはディレクトリ構造がそのままパッケージの階層になってます。

また、ディレクトリ直下の `__init__.py` はその階層に属するパッケージが最初に `import` された際に呼び出されます。

そのため、

* 単体ファイル module
* `__init__.py` だけ配置された module ディレクトリ

は同じ挙動になります。

そのため「最初は単体ファイルから書き始めて、コード量が増えてきたらディレクトリに切り出して `__init__.py` に移す」 といったことがスムーズにできるわけです。

## 単体ファイル module

```bash
$ tree
.
└── hoge.py
```

```py:hoge.py
print("file module")

def method1():
    print("file method1")
```

```bash
$ python -c "import hoge; hoge.method1()"
file module
file method1
```

## ディレクトリ module

```bash
$ tree
.
└── hoge
    ├── __init__.py
    └── m1.py
```

```py:hoge/__init__.py
print("directory module")

from .m1 import method1
```

```py:hoge/m1.py
def method1():
    print("directory method1")
```

```bash
$ python -c "import hoge; hoge.method1()"
directory module
directory method1
```

## ちなみに

python2 では `__init__.py` は必須でした

```bash
$ tree foo/
foo/
└── bar.py
```

```bash:python2
$ python
Python 2.7.15 (default, Mar 19 2019, 13:35:07)
[GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.10.44.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import foo.bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named foo.bar
```

が、python3 ではその縛りはなくなりました。

```bash:python3
$ python
Python 3.7.2 (default, May 16 2019, 10:59:10)
[Clang 10.0.0 (clang-1000.10.44.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import foo.bar
```

# 相対 import

import を実行する場所からみて、 `.` を並べる数で上の階層を辿っていきます。

* `.` がその階層の他のパッケージ
    * 下図で言うと `foo/bar/__init__.py` から見た `foo/bar/a.py`
* `..` で1階層上
    * 下図で言うと `foo/bar/__init__.py` から見た `foo/a.py` や `foo/baz/__init__.py`
    * `__init__.py` と ディレクトリmoduleの関係を思い出してください
* 以下同じ

```bash
$ tree
foo/
├── __init__.py
├── a.py
├── bar
│   ├── __init__.py
│   ├── a.py
└── baz
    ├── __init__.py
    └── a.py
```

```py:foo/bar/a.py
from ..baz import a

print(1111)
```

```py:foo/baz/a.py
print(222)
```

```bash
$ python -c "import foo.bar.a"
222
1111
```

また、インタプリタ実行場所よりも上の階層は読めません。

```bash:python3
$ cd foo/bar/ && python -c "import a"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/Users/uu147969/workspace/qiita/foo/bar/a.py", line 1, in <module>
    from ..baz import a as baza
ImportError: attempted relative import with no known parent package
```

# `__init__.py` に import を書く場合

※以下ポエム要素あり

`__init__.py` に `__all__` を置くことで、その module で何を公開するかを指定する事ができます。
https://qiita.com/suzuki-kei/items/8fea67655abf216a5013

これを、パッケージの最上位の `__init__.py` に置いて、実質パッケージ内の全ての module を `import` するケースがよくありますが、これはあまりおすすめできません。
前述した通り、その module を触った（最初に `import` しようとした）時点で `__init__.py` の内容が走るので、芋づる式に必要のない module まで `import` されてしまうためです。
また、 `import foo.bar.a` とした場合にも途中のスキップはできず、 `foo/__init__.py` `foo/bar/__init__.py` 全て走ります。

[The Zen of Python](https://www.python.org/dev/peps/pep-0020/) に

> Explicit is better than implicit.
> Namespaces are one honking great idea -- let's do more of those!

とあります。

`from foo import *` とかやってしまうと namespace が台無しになる上、「この変数どこで定義されてるの？」「もしくはどこで import されたの？」の切り分けが難しくなります。
module 分割は粒度が難しいところですが、論理的に意味を成す単位で切り出して、利用する側で明示的に必要な module を `import` するような実装を心掛けるのが良いと思います。

# 参考： module 設計の具体例

python2 の urllib が、場当たり的に対応してたらこうなっちゃった残念感満載だったところを、
https://github.com/python/cpython/blob/2.7/Lib/urllib.py
https://github.com/python/cpython/blob/2.7/Lib/urllib2.py
https://github.com/python/cpython/blob/2.7/Lib/urlparse.py

python3 で綺麗に書き直されています。
https://github.com/python/cpython/tree/3.7/Lib/urllib

おわり。