2.19. モジュールを書く¶
どんな .py ファイルもモジュールです。成長していくスクリプトをいくつかのファイルに分割すると、各ファイルが短く保たれ、共通のヘルパーをスクリプト間で共有できるようになります。
2.19.1. スクリプトを分割する¶
関連する関数のグループを、それら自身のファイルに取り出します。
camera_utils.py:
def banner():
print("OpenMV")
def label(text):
return "[" + text + "]"
main.py:
import camera_utils
camera_utils.banner()
print(camera_utils.label("ready"))
出力:
OpenMV
[ready]
2つのファイルは同じディレクトリ内に並んで置かれます。main.py が実行されると、import camera_utils は camera_utils.py を一度読み込み、そのトップレベルの文を実行し、結果として得られるモジュールオブジェクトを main 内の名前 camera_utils に束縛します。他のどこからの2回目の import camera_utils も、同じオブジェクトを返します。モジュールは最初の読み込み後にキャッシュされるためです。
モジュールの名前はそのファイル名に由来するので、camera_utils.py は import camera_utils としてインポートされます。
2.19.2. 複数ファイルのモジュール(パッケージ)¶
モジュールは、単一の .py ではなく、ファイルの ディレクトリ であることもできます。ディレクトリの名前がモジュール名となり、その中のファイルがその サブモジュール となります。
camera_utils/
__init__.py
text.py
timing.py
__init__.py は、パッケージ自体がインポートされたときに実行されるファイルです。空でもよく、サブモジュールから選ばれた名前を再エクスポートすることもできます。サブモジュールにはドット付きの名前でアクセスします。
import camera_utils.text
from camera_utils.timing import elapsed
camera_utils.text.label("ready")
パッケージの内部では、サブモジュールどうしは完全なドット付き名前でも、あるいは先頭のドットで「このパッケージ」を意味する 相対インポート でも、互いにアクセスできます。
camera_utils/timing.py:
from . import text # the sibling submodule
def stamp(value):
return text.label(str(value))
代わりに特定の名前を取り込むには、ドット付きの兄弟モジュールに続けてその名前を指定します。
from .text import label
def stamp(value):
return label(str(value))
相対インポートはパッケージを自己完結に保ちます。パッケージのディレクトリ名を変更しても、すべてのサブモジュールを編集する必要はありません。
単一のファイルが快適なサイズを超えて成長したとき、または関連するモジュールのまとまりが1つの名前空間のもとに属するべきときには、パッケージを使います。日常的なスクリプトには、単一の .py ファイルで十分です。
2.19.3. __name__ガード¶
すべてのモジュールには、組み込みの名前 __name__ があります。その値は、ファイルがどのように使われているかによって変わります。
ファイルが 直接実行 される場合、
__name__は文字列"__main__"に設定されます。ファイルが別のスクリプトによって インポート される場合、
__name__はモジュール名、つまり.pyを除いたファイル名に設定されます。
これを使ったイディオムは次のとおりです。
label_util.py:
def label(text):
return "[" + text + "]"
if __name__ == "__main__":
print(label("self-test"))
出力(直接実行した場合):
[self-test]
同じファイルが代わりに インポート される場合、__name__ はモジュール名に設定されるので、if ブロックはスキップされ、余分なものは何も実行されません。
>>> import label_util
>>> label_util.__name__
'label_util'
このパターンを使うと、ライブラリファイルに手早いスモークテストやデモを付けても、それをインポートするスクリプトを妨げずに済みます。