パッケージの基本構造
Pythonでは、コードの組織単位としてモジュールとパッケージが用いられます。モジュールは単一の.pyファイルですが、複数のモジュールやサブパッケージをまとめるために「パッケージ」を使用します。ディレクトリ内に__init__.pyファイルが存在する場合、そのディレクトリはPythonによってパッケージとして認識されます。
__init__.pyは空ファイルでも構いませんし、初期化コードを含んでいても構いません。このファイルの有無が、ディレクトリがパッケージとして扱われるかどうかの分岐点です。
パッケージのインポートと初期化動作
パッケージが初めてインポートされた際、対応する__init__.py内のコードが実行されます。ただし、一度読み込まれたパッケージはキャッシュされるため、再度インポートしても__init__.pyの内容は再実行されません。
したがって、__init__.pyには副作用のある実行文ではなく、関数やクラスの定義、あるいは他のモジュールのインポート処理を記述することが推奨されます。
インポート時の名前解決順序
以下のようなインポート文を考えます:
from mypackage import something
このとき、Pythonは以下の順でsomethingを探します:
-
mypackage/__init__.py内で定義された変数・関数・クラス -
mypackage/something/というサブディレクトリ(サブパッケージ)が存在するか -
mypackage/something.pyというモジュールファイルが存在するか
どれにも該当しない場合はImportErrorが発生します。
具体例:mypackageの構成
以下のような構成のパッケージを想定します:
mypackage/ ├── __init__.py ├── core.py └── utils.py
core.pyの内容:
def process_data():
print("Processing data in core module")
utils.pyの内容:
def helper_func():
print("Helper function from utils")
この状態でインタラクティブシェルから操作すると:
>>> import mypackage
>>> mypackage.core
AttributeError: module 'mypackage' has no attribute 'core'
これは、__init__.pyが空のため、coreモジュールがmypackage名前空間に読み込まれていないためです。
自動インポートの実現方法
上記のエラーを回避し、import mypackageだけでmypackage.coreやmypackage.utilsを使えるようにするには、__init__.pyに次のように記述します:
# mypackage/__init__.py
from . import core
from . import utils
print("mypackage initialized")
これにより、以下のように利用可能になります:
>>> import mypackage
mypackage initialized
>>> mypackage.core.process_data()
Processing data in core module
大規模なパッケージ構成での活用
以下のような音声処理ライブラリを想定します:
audio_toolkit/
├── __init__.py
├── engine/
│ ├── __init__.py
│ └── processor.py
├── io/
│ ├── __init__.py
│ └── wav_handler.py
└── effects/
├── __init__.py
└── reverb.py
トップレベルのaudio_toolkit/__init__.pyに以下を記述することで、ユーザーが簡単に機能にアクセスできるようになります:
"""Audio processing toolkit main package"""
from . import engine
from . import io
from . import effects
# よく使う関数をトップ名前空間に展開
from .engine.processor import process_audio
from .io.wav_handler import load_wav, save_wav
これにより、利用者は次のように簡潔に使用できます:
>>> import audio_toolkit as atk
>>> data = atk.load_wav("input.wav")
>>> processed = atk.process_audio(data)
>>> atk.save_wav("output.wav", processed)
相対インポートの活用
サブパッケージ内部では、相対インポートを使って他モジュールを参照できます。例えばaudio_toolkit/effects/__init__.py内で:
from ..engine import processor
from ..io import wav_handler
この記法により、親パッケージ内のモジュールを明確に参照できます。ドット(.)は現在のパッケージを、..は一つ上の階層を意味します。