"BOKU"のITな日常

BOKUが勉強したり、考えたことを頭の整理を兼ねてまとめてます。

Python3のモジュールとパッケージの違いおよびimportについて

f:id:arakan_no_boku:20210506231402p:plain

目次

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

今回はPythonの「import」についてです。

なんですが。

Pythonのimportを理解するには、まず「モジュール」と「パッケージ」の違いの整理が必要なので、そこからやります。

モジュールはPythonのソースファイルです。

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

パッケージはモジュールを、packname.modulenameのような "ドット付きモジュール名" を使って参照できるようにする手段でのことです。

この「packname」にあたる部分は、Windowsでは「フォルダ」のことです。

つまり、上記は「packname」フォルダの「modulename.py」ファイルを参照するという意味になります。

でも、フォルダを作ってそこにソースファイルをただ置けば良いわけでなありません。

フォルダをパッケージとして扱えるようにするには、そのフォルダに「__init__.py」というファイルを置かねばならないというルールがあります。

この「__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で指定されたモジュールは以下の順序で検索されます。

  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なんかで凝る必要はないと思っているので、今回はごく基本的なことだけまとめてます。

ではでは。