Pythonインポート周り徹底理解への道

## 概要

自分でPythonのファイルを分割する場合に、
いつも悩むので、一度しっかりまとめようと思ったので、まとめました。
同じ悩みの人のお役に立てれば幸いです。

※以降の説明はすべて、main.pyが実際に実行するPythonファイルとなります。

## インポートの基礎知識

Pythonのライブラリは主に以下の3種類となります。

* 標準ライブラリディレクトリ
* site-packagesディレクトリ(pip installでインストール)
* ユーザ別site-packagesディレクトリ(pip install --userでインストール)

Pythonが読み込むライブラリの場所を知りたい場合は、下記の様に実行すれば分かります。

```shell

user$ python
Python 3.6.8 (default, Mar 10 2019, 22:43:46)
[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 sys
>>> sys.path
```
なお、独自のインポートパスを追加したい場合は、以下のようにすれば追加できます。
(※<>の記号は不要です)

```shell
export PYTHONPATH="<追加したいパス>:$PYTHONPATH"
```

## STEP1: .pyファイルのインポート(同階層)

一番基礎的なパターンです。
同じ階層に存在する、.pyファイル内の関数やクラスをインポートするパターンです。

```:フォルダ構成
.
├── lib_a.py
└── main.py
```

```python:main.py
from lib_a import Student, add

taro = Student('taro', 25)
print(taro.age)
print(add(3, 8))
```

```python:lib_a.py
class Student:
    def __init__(self, name: str, age: int):
        self.__name: str = name
        self.__age: int = age

    @property
    def name(self) -> str:
        return self.__name

    @property
    def age(self) -> int:
        return self.__age

    @name.setter
    def name(self, name: str) -> None:
        self.__name = name

    @age.setter
    def age(self, age: int) -> None:
        self.__age = age

def add(x: float, y: float) -> float:
    return x + y

```

```shell
# 正常にインポートできている
$ python main.py
25
11
```


## STEP2: .pyファイルのインポート(別階層)

同階層以外の場所のPythonファイルをインポートする場合は、環境変数PYTHONPATHにインポート対象のパスを追加する必要があります。

```:フォルダ構成
.
└── main.py

~/Downloads
└── lib_a.py
```

main.pyとlib_a.pyはSTEP1と同じものを利用します

```shell
# 正常にインポートできている
$ export PYTHONPATH="/Users/username/Downloads:$PYTHONPATH"
$ python main.py
25
11
```

## STEP3: 1階層のパッケージをインポートする

Pythonにはモジュールとパッケージという概念があります。
ざっくり説明しますと、モジュールは単一のpythonファイル(.py)で、パッケージはモジュールが複数含まれているフォルダ、という理解でいいかと思います。

今回は1階層のパッケージ内のモジュールをインポートする方法を見てみます。

```:フォルダ構成
.
├── lib
│   └── lib_a.py
└── main.py
```

lib_aは同じものを使用します。
libパッケージ内のlib_aモジュールをインポートします。

```python:main.py
from lib.lib_a import Student, add

taro = Student('taro', 25)
print(taro.age)
print(add(3, 8))
```

```shell
# 正常にインポートできている
$ python main.py
25
11
```

### Pythonのインポート方法に関して

Pythonにおけるインポートの方法は2種類存在します。

* from ... import ... 方式
* import ... 方式

上記はfrom ... import ... 方式ですが、下記のようにimport方式でも問題なくインポート可能です。
ですが、インポートしたクラスや関数を使うためには、クラス名の前にパッケージ名などを明記する必要があります。

```python:main.py
import lib.lib_a

# クラスを使うためには、パッケージ名、モジュール名を付ける必要がある
taro = lib.lib_a.Student('taro', 25)
print(taro.age)
print(lib.lib_a.add(3, 8))
```

ここで、下記のようにしてもインポートできるのでは？、と思われた方がいらっしゃるかもしれません。

```python:main.py
# libのみをインポート
import lib

taro = lib.lib_a.Student('taro', 25)
print(taro.age)
print(lib.lib_a.add(3, 8))
```

こちらの方法では、インポートは出来ません。
Pythonのインポートでは、 **インポートしたパッケージ内のモジュールは自動的にインポートされない**という制約があるためです。
今回の場合では、libパッケージのみをインポートしても、内部のlib_aモジュールは自動的にはインポートされない、ということになります。

libのみをインポートして、lib_aモジュールをインポートする方法も存在しますので、
そちらを紹介します。
Pythonのパッケージには\_\_init\_\_.pyというファイルが必要となります。
(Python3系では、必須ではありません)
Pythonのパッケージをインポートした場合、この\_\_init\_\_.pyというファイルがまず実行されます。

\_\_init\_\_.pyファイル内に、lib_aをインポートする、という記述を書けば、main.py内でlibパッケージをインポートするだけで、自動的にlib_aモジュールがインポートされるようになります。


```:フォルダ構成
.
├── lib
│   ├── __init__.py
│   └── lib_a.py
└── main.py
```

```python:__init__.py
from . import lib_a
```

```python:main.py
# libをインポートする際に、__init__.py内で、lib_aをインポートしている
import lib

taro = lib.lib_a.Student('taro', 25)
print(taro.age)
print(lib.lib_a.add(3, 8))
```

```shell
# 正常にインポートできている
$ python main.py
25
11
```


## STEP4: 複数階層のパッケージをインポートする

パッケージ内に複数の内部パッケージを含むような場合を考えてみます。

```:フォルダ構成
.
├── lib
│   ├── liba
│   │   └── lib_a.py
│   └── libb
│       └── lib_b.py
└── main.py
```

```python:main.py
from lib.liba.lib_a import Student, add

taro = Student('taro', 25)
print(taro.age)
print(add(3, 8))

# 追記
taro.print()

```

```python:lib_a.py
from lib.libb.lib_b import b_print
# 下記のように相対インポートでも可
# from ..libb.lib_b import b_print

class Student:
    def __init__(self, name: str, age: int):
        self.__name: str = name
        self.__age: int = age

    @property
    def name(self) -> str:
        return self.__name

    @property
    def age(self) -> int:
        return self.__age

    @name.setter
    def name(self, name: str) -> None:
        self.__name = name

    @age.setter
    def age(self, age: int) -> None:
        self.__age = age

    def print(self) -> None:
        b_print()

def add(x: float, y: float) -> float:
    return x + y
```

```python:lib_b.py
def b_print():
    print('module b execute print')
```

上記のフォルダ構成を見てください。
main.pyからlibaパッケージ内lib_aモジュールのStudentクラスのprint関数を呼び出します。
このprint関数は、libbパッケージ内lib_bモジュールのb_print関数を呼ぶ、という流れとなっています。

**Pythonでは実行対象のPythonファイルの同階層以外のモジュールを呼び出すことが出来ない**という制約があります。
例えば、main.pyの一つ上のディレクトリの.pyファイルをインポートすることは出来ません。
(もちろん、PYTHONPATHで追加すれば、インポートは可能です)
ですが、パッケージ内に限り、この制約を逃れることが可能です。
つまり、lib_aモジュールの上の階層に存在するlibbパッケージ内のlib_bモジュールをインポート(絶対インポート・相対インポート)することが可能です。
(もちろん、PYTHONPATHなどは使いません)

パッケージ最上位からすべて指定する方法が絶対インポートと呼ばれます。
今回はlibが最上位なので、「lib.libb.lib_b」となります。
`from lib.libb.lib_b import b_print`

代わりに、インポートしたいファイルを起点にして、指定する方法を相対インポートと呼びます。
lib_aから見ると、一つ上の階層にあるlibbを指定したいので、ドットを２つ繋げて、「..libb.lib_b」となります。
ドットが一つの場合は、同階層という意味になります。
`from ..libb.lib_b import b_print`

```shell
# 正常にインポートできている
$ python main.py
25
11
module b execute print
```

## 最後に

本記事の内容で自分でパッケージを作成・インポートする場合の基礎は理解できるのではないかと思います。
その他のパターンももしありましたら、教えていただければと思います。
ちなみに、アスタリスクでインポートする場合は、別の注意点が必要のようですので、下記記事を参照ください。
本記事が少しでも皆さんの助けになれば幸いです。

[［Python入門］パッケージ (1/2)](https://www.atmarkit.co.jp/ait/articles/1907/09/news010.html)