216
212

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-07-15

概要

自分で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)

216
212
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
216
212

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?