目次
Pythonの「import」について
今回はPythonの「import」についてです。
概要の説明だと混乱しがちなので、簡単なサンプルで注意点と検索の順序などを整理してみようと思います。
importの話をするには、まず「モジュール」と「パッケージ」の違いの整理が必要なので、そこからやります。
モジュールとパッケージの違い
ひとつのPythonのソースファイル(.pyファイル)を「import」で参照するとき、そのソースファイルを「モジュール」と呼びます。
そのモジュールをまとめて扱えるのが「パッケージ」です。
パッケージには「通常パッケージ」と「名前空間パッケージ」があります。
通常パッケージ
Python2,3共通で使えます。
パッケージはモジュールとなるソースを置くフォルダのことです。
このフォルダを「パッケージ」として使うために「__init__.py」 という名前のファイルを配置する方法をとるのが「通常パッケージ」です。
__init__.py にはパッケージの初期化処理を記述しますが、空でもかまいません。
たとえば「modulename」というモジュールのソースは「modulename.py」です。
そのソースを「packname」というフォルダにおいた場合、「packname.modulename」のように参照することができます。
名前空間パッケージ
名前空間パッケージは「配布物」を作る場合に、単一のパッケージの中のサブパッケージやモジュールを分離させるような場合に使います。
例えばPythonで開発しているライブラリのようなものがあって、対象製品別に配布するサブパッケージやモジュールを分ける必要があるときなのに、これを使えばいいよということなのですが、ちょっとわかりづらいです。
名前空間パッケージを実現する方法は3つあります。
組み込むの名前空間パッケージはPython3.3以降しか使えません。
これを使うときにはすべてのパッケージから「__init__.py」を削除します。
pkgutil 型の名前空間パッケージ はPython2でも使えます。
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
だけを含んだ「__init__.py」を置く必要があります。
pkg_resources 型の名前空間パッケージ は、今は推奨されていないのですが、既存の名前空間パッケージで最も多く使われている方法でもあります。
__import__('pkg_resources').declare_namespace(__name__)
だけを含んだ「__init__.py」を置く必要があります。
名前空間パッケージは「配布物」全体で「同一の __init__.py 」を持つのが前提です。
一部だけ適用も、混在もできませんから、よほどの必要のある現場以外で使われているのを見たことがありません。
なので、名前空間パッケージはさわりの説明だけで、以降はふれません。
import
ここからは「通常パッケージ」を前提に話をすすめます。
上記のモジュールとパッケージをソース内で参照するときの書き方の整理です。
モジュールのimport
動作確認のため、こんな感じで適当なソースファイルを作ります。
def mod01():
print("モジュール01をインポート")
これを「imp_mod01.py」という名前をつけて保存します。
これで、このファイルは「imp_mod01」モジュールでimportできます。
例えば、同じフォルダにもうひとつソースを作って、その中で上記のモジュール(imp_mod01)をimportして、その中のmod01()メソッドを呼びます。
import imp_mod01
# mod01モジュールを呼ぶ
imp_mod01.mod01()
これを保存して実行すると。
モジュール01をインポート
と表示されるので、モジュールが正しくimportされたのがわかります。
importで指定されたモジュールは以下の順序で検索されます。
- その名前のビルトインモジュール。
- 見つからなかった場合は sys.path にあるフォルダのリスト
この「sys.path」 のフォルダのリストには、以下がはいります:
- 入力されたスクリプトのあるフォルダ またはカレントフォルダ
- PYTHONPATH
- インストールごとのデフォルト。
上記の「imp_mod01」の場合は、sys.pathの「1.入力されたスクリプトのあるフォルダ」で見つかったというわけです。
モジュールのimportでよくある間違いが「from . import imp_mod01」のように「カレントフォルダを示すのにドットを書いてしまう」というものです。
もしモジュールに対してこう書いてしまうと。
ImportError: attempted relative import with no known parent package
のエラーでにっちもさっちもいかなくなります。
このような書き方は「パッケージ」に対してのみ可能な書き方だからです。
パッケージのimport
動作確認のため、以下のようなフォルダとファイルを作ります。
フォルダは赤字、ファイルは黒字です。
__init__.py は空ファイルでいいです。
imp_pac01.pyの中身は
def pac01():
print("パッケージの1階層目です")
imp_pac02.pyの中身は
def pac02():
print("パッケージの2階層目です")
とでもしておきます。
このようにサブフォルダを作り、そこに「__init__.py」を置いたものがパッケージです。
これらのサブフォルダの下のモジュールをimportする場合は
#一階層目
import pack01.imp_pac01 as ip
#ニ階層目
import pack01.pack02.imp_pac02 as ip2
のように「.」(ドット)で連結してインポートできます。
もしくは、fromでパッケージを指定して
#一階層目
from pack01 import imp_pac01 as ip
#ニ階層目
from pack01.pack02 import imp_pac02 as ip2
みたいな感じにします。
パッケージ(ようするにサブフォルダの中)に複数のモジュール(ようするにPythonのソース)がある場合や、ひとつ上の階層のフォルダにいるモジュールをインポートする場合は以下のようにfromとドット(.)で指定する仕方もできます。
同じ階層の他のモジュールをインポート
from . import imp_pac01_other as ipo
ひとつ上の階層にあるモジュールをインポート
from .. import imp_pac01 as ip
ちなみに。
パッケージを import する際 は sys.path 上のフォルダを検索して、トップレベルのパッケージの入ったサブフォルダを探します。
ここでいうsys.path 上のフォルダというのは、前に書いたのと同じく
- 入力されたスクリプトのあるフォルダ またはカレントフォルダ
- PYTHONPATH
- インストールごとのデフォルト。
がはいっています。
以上です。
ではでは。