おぼえがき

過ちては則ち改むるにうんぬんかんぬん

パッケージとモジュールについて(Python)

f:id:feci:20210726231508j:plain

Python3

 

Python3.8.5におけるパッケージとモジュールについて。

 

utils.pyのコード(今回importされる側のモジュール)。

def aaa(a):
  return (a + '!' ) * 2

utils.pyはlesson_packageファイル内に存在しています。

 

 

sample.pyのコード(今回importする側のモジュール)。

#①import文でモジュールをインポート
import lesson_package.utils  #フルパスでutilsモジュールを読み込み

bbb = lesson_package.utils.aaa(str(123))
print(bbb)  # 123!123!


#②fromで①からモジュールのパスを省略
from lesson_package import utils  #fromでutilsモジュールを読み込み

ccc = utils.aaa('ふんてこわ')     #パッケージの記述なしのモジュールだけでOK
print(ccc)                        #ふんてこわ!ふんてこわ!


#③utils.pyからaaa関数だけをインポート(推奨されない方法)
from lesson_package.utils import aaa #utilsモジュールからaaa関数だけを読み込み

ddd = aaa('あいうえお')              #aaaがどこから来た関数なのか判別が難しい
print(ddd)                           #あいうえお!あいうえお!


#④asでモジュールの名前を変更する(推奨されない方法)
from lesson_package import utils as u  #utilsをuに改名

eee = u.aaa("ねむい")                  #なんのモジュールなのか判別が難しい
print(eee)                             #ねむい!ねむい!

 

今回はパッケージとモジュールについての解説なので、複数のPythonファイルがでてきます。

 

今回つかうモジュールはsample.pyとutils.pyの2つです。

utils.pyはlesson_packageフォルダ内に格納されています。

lesson_packageフォルダとsample.pyは同じ階層に存在しています。

 

 

モジュールとは、Pythonファイルのことです。

なので今回つかうモジュールは、sample.pyとutils.pyの2つということになります。

 

パッケージは、複数のモジュールをまとめたフォルダのことです。

なので今回つかうパッケージは、utils.pyと__init__.pyを格納しているlesson_packageフォルダということになります。

 

ちなみに、__init__.pyはモジュールをインポートして使う場合に必須のモジュールらしいです。

この名前のモジュールがimportされる側のモジュールが格納されているパッケージ内に存在しないと、importが上手くいかなくなるようです。

 

 

上記のプログラムでは、utilsモジュールをsample.pyにimportして、utilsモジュール内のaaa関数をsample.py上で実行しています。

 

フルパスでモジュールを読み込む場合は、①のパターンのようにimport文にパッケージとモジュール名を記述します。

ただしこの場合は、aaa関数を実行する際に常にパッケージとモジュール名の記述が必要になります。

 

②のパターンでは、fromを使うことでaaa関数の実行時の記述量を減らすことができます。

この場合は、aaa関数の実行時にはモジュール名を記述するだけでOKとなります。

 

③のパターンでは、utilsモジュールからaaa関数のみをimportしています。

ただしこのパターンは、Pythonのプログラミングとしては推奨されている方法ではありません。

なぜならこの方法だと、aaa関数がどこから来た関数なのかがコードから判別しづらいからです。

 

 

また④のパターンのように、asを使うことでimportするモジュールの名称を任意のものに変更することもできます。

ただし、この方法もPythonのプログラミング的には推奨されている方法ではありません。

 

なぜならこの方法だと、aaa関数がどこのモジュールから読み込んできた関数なのかがコードから判別しづらいからです。

 

 

また下記のプログラムのように、より深いディレクトリからモジュールをimportすることもできます。

#①絶対パスでより深いディレクトリからのimport
from lesson_package.deep import utils02 #lessフォルダ内のdeepフォルダ内のutils02をimport

z = utils02.aaa()                       #utils02モジュールのaaa関数の実行
print(z)

 

このプログラムでは、sample.pyと同じディレクトリにあるlesson_packageフォルダ内に作ったdeepフォルダの中にあるutils02.pyをimportしています。

このようにより深い階層にあるモジュールをimportする場合はフォルダ名の後ろにピリオドを記述して順次フォルダ名を追加していきます。

 

ちなみに、このような形で絶対パスを記述してもimportできますが、Pythonでは相対パスでもモジュールをimportすることはできます。

 

相対パスは、importする側のモジュールのディレクトリの位置を基準としたパスになります。

相対パスで1つ上のディレクトリに戻る場合は、ピリオドを2つ続けて記述します。

 

この相対パスの記述ですが、これもPythonのプログラミングでは推奨された方法ではありません

なぜなら、相対パスではぱっと見てディレクトリと当該モジュールの関係が理解しづらいからです。

 

 

ちなみにこの一連のimportですが、これはaモジュールをimportしたbモジュールをcモジュールでimportしてcモジュールでaモジュールの関数を実行する、といったややこしいことも可能です。

 

 

import *と__all__で読み込まれるモジュールを指定する方法について。

#①import *と__all__で読み込まれるモジュールを指定
from lesson_package.deep import * #lessフォルダ内のdeepフォルダ内のモジュールをimport

z = utils02.aaa(), utils03.bbb()  #utils02とutils03モジュールの関数のそれぞれ実行
print(z)

上記のスクリプトのようにimport *と記述することで、importするモジュールをコントロールすることもできます。

 

 

この方法でモジュールをimportするには、下記の2つの条件を満たさなければいけません。

 

  1. importしたいモジュールがあるディレクトリにある__init__.pyに __all__ = ['モジュール名1', 'モジュール名2'] と記述する。
  2. fromで指定したディレクトリ内にimportしたいモジュール(読み込まれるファイル)が存在すること。

 

 

なので上記①のスクリプトは、lwsson_packageフォルダ内にあるdeepフォルダ内に__init__.pyとutils02.pyとutils03.pyの3つのモジュールが存在していることになります。

そしてその__int__.pyに __all__ = ['utils02', 'utils03'] と記述することで、from lesson_package.deep import *と記述したモジュール内でその2つのモジュール内の関数を実行することが可能になります。

 

しかしこの方法は、モジュールをimportする方法としてはあまり推奨されてはいません。

この方法では視覚的にパッと見てどのモジュールをimportしたのかが分かりづらいので、あまり使わないほうがいいとされています。

 

 

try-except文でのImportErrorの例外の処理について。

#①try-excpt文でImportErrorの例外を処理する
try:
  from lesson_package import utils02      #古いパッケージ・使えない場合は下へ
except ImportError:
  from lesson_package.deep import utils02 #新しいパッケージ・上がダメな場合はこれ

z = utils02.aaa()                         #どっちのパッケージでも実行できる
print(z)

importのパスが間違っているときは、ImportErrorが出てスクリプトが終了します。

 

それを防ぐために、上記スクリプトのようにtry-except文でImportErrorの例外を処理することもできます。

このような処理の場合、tryのパッケージが使える場合はそれでaaa関数を実行しますし、もし最初のパッケージのパスが変わっているなどでErrorが出た場合は、exceptのほうのパッケージがimportされます。

 

なお当然ですが、import先のパスが2つとも間違っていた場合はImportErrorの例外を処理できませんので、そのErrorが出た時点でスクリプトは強制停止します。

 

 

ちなみに、複数のモジュールを集めたものがパッケージですが、複数のパッケージを集めたものはライブラリと呼ばれます。

 

 

以上です。