"BOKU"のITな日常

興味のむくまま気の向くままに調べたり・まとめたりしてます。

Python3のモジュールとパッケージの違いおよびimportに関するまとめ

f:id:arakan_no_boku:20210506231402p:plain

目次

モジュールとパッケージの違い

Pythonのimportを理解するのに「モジュール」と「パッケージ」の区別は大切です。

モジュールはざっくりいえば、Pythonのソースファイルそのものです。

Pythonのソースファイル名は「モジュール名.py」ですから。

パッケージはモジュールを、packname.modulenameのような "ドット付きモジュール名" を使って参照できるようにする手段で、対象とするフォルダをパッケージとして扱えるようにするには「__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で指定されたモジュールは以下の順序で検索されます。

  1. その名前のビルトインモジュール。
  2. 見つからなかった場合は sys.path にあるフォルダのリスト

この「sys.path」 のフォルダのリストには、以下がはいります:

  1. 入力されたスクリプトのあるフォルダ またはカレントフォルダ
  2. PYTHONPATH
  3. インストールごとのデフォルト。

上記の「imp_mod01」の場合は、sys.pathの「1.入力されたスクリプトのあるフォルダ」で見つかったというわけです。

モジュールのimportでよくある間違いが「from . import imp_mod01」のように「カレントフォルダを示すのにドットを書いてしまう」というもので、もしモジュールに対してこう書いてしまうと。

ImportError: attempted relative import with no known parent package

のエラーでにっちもさっちもいかなくなります。 

このような書き方は「パッケージ」に対してのみ可能な書き方だからです。

パッケージのimport

ということで。

今度はパッケージです。

動作確認のため、以下のようなフォルダとファイルを作ります。

フォルダは赤字、ファイルは黒字です。

f:id:arakan_no_boku:20210507001534p:plain

__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 上のフォルダというのは、前に書いたのと同じく

  1. 入力されたスクリプトのあるフォルダ またはカレントフォルダ
  2. PYTHONPATH
  3. インストールごとのデフォルト。

がはいっています。

まとめ

モジュールとパッケージおよびインポートについてまとめました。

実際は、sys.pathをあとで書き換えるなんて荒業もできるので、importのバリエーションはもっとたくさんあります。

でも、個人的に、あんまりimportなんかで凝る必要はないと思っているので、今回はごく基本的なことだけまとめてます。

ではでは。