Edited at

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


概要

自分でPythonのファイルを分割する場合に、

いつも悩むので、一度しっかりまとめようと思ったので、まとめました。

同じ悩みの人のお役に立てれば幸いです。

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


インポートの基礎知識

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


  • 標準ライブラリディレクトリ

  • site-packagesディレクトリ(pip installでインストール)

  • ユーザ別site-packagesディレクトリ(pip install --userでインストール)

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


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

なお、独自のインポートパスを追加したい場合は、以下のようにすれば追加できます。

(※<>の記号は不要です)

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


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

一番基礎的なパターンです。

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


フォルダ構成

.

├── lib_a.py
└── main.py


main.py

from lib_a import Student, add

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



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


# 正常にインポートできている

$ python main.py
25
11


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

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


フォルダ構成

.

└── main.py

~/Downloads
└── lib_a.py


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

# 正常にインポートできている

$ 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モジュールをインポートします。


main.py

from lib.lib_a import Student, add

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


# 正常にインポートできている

$ python main.py
25
11


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

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


  • from ... import ... 方式

  • import ... 方式

上記はfrom ... import ... 方式ですが、下記のようにimport方式でも問題なくインポート可能です。

ですが、インポートしたクラスや関数を使うためには、クラス名の前にパッケージ名などを明記する必要があります。


main.py

import lib.lib_a

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


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


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


__init__.py

from . import lib_a



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))


# 正常にインポートできている

$ python main.py
25
11


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

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


フォルダ構成

.

├── lib
│   ├── liba
│   │   └── lib_a.py
│   └── libb
│   └── lib_b.py
└── main.py


main.py

from lib.liba.lib_a import Student, add

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

# 追記
taro.print()



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



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を指定したいので、ドットを2つ繋げて、「..libb.lib_b」となります。

ドットが一つの場合は、同階層という意味になります。

from ..libb.lib_b import b_print

# 正常にインポートできている

$ python main.py
25
11
module b execute print


最後に

本記事の内容で自分でパッケージを作成・インポートする場合の基礎は理解できるのではないかと思います。

その他のパターンももしありましたら、教えていただければと思います。

ちなみに、アスタリスクでインポートする場合は、別の注意点が必要のようですので、下記記事を参照ください。

本記事が少しでも皆さんの助けになれば幸いです。

[Python入門]パッケージ (1/2)