sec05 - Appendix 3:Pythonパッケージの基礎:__init__.pyと__all__の使い方をわかりやすく解説

このレクチャーでは、Pythonのパッケージ構造を整理する上で欠かせない「__init__.py」の役割について解説します。

特に初学者が混乱しやすい「import *」の挙動や「__all__」の使い方についても、具体例を交えながら理解していきましょう。

スポンサーリンク

__init__.py とは?Pythonパッケージの基本構造を理解する

__init__.pyは、Pythonが「このフォルダはパッケージですよ」と認識するための特別なファイルです。

たとえば、次のような構成を考えます。

project/
├─ main.py
└─ my_package/
   ├ __init__.py
   ├ module_a.py
   └ module_b.py

my_package/__init__.pyが存在することで、main.pyに記述した、次のようなインポートを許可します。

import my_package.module_a
from my_package import module_b

もし__init__.pyが存在しなければ、Pythonはそれを「ただのフォルダ」とみなし、パッケージとして認識しません。

Python 3.3以降で__init__.pyが不要になる理由:"暗黙の名前空間パッケージ"とは

厳密には、Python 3.3以降では__init__.pyがなくてもパッケージとして機能する『暗黙の名前空間パッケージ(Implicit Namespace Packages)という仕組みが存在します。しかし、これは高度な利用法であり、多くのツール(LinterやIDE)は__init__.pyがあることを前提としているため、プロジェクトの可読性と互換性のためにも、設置することが強く推奨されます。

__init__.pyには何を書くべきか:基本と実例解説

こちらのファイルをダウンロードし、各ファイルの中身を確認してください:


__init__.pyの中身は「空のファイルでも構いません」。しかし、プロジェクトが大きくなるほど、__init__.pyには“初期化処理”や“便利なエクスポート設定”を記述することが増えます。

__init__.py を使ってパッケージの公開APIを整理する方法

たとえば、パッケージ内でよく使うクラスや関数をまとめておくと、次のように「パッケージ全体の顔」として振る舞わせることができます。

# __init__.py
from .module_a import func_a
from .module_b import ClassB

これで、外部(main.py)からは次のようにシンプルにアクセスできます。

from my_package import func_a, ClassB

__init__.py が import の仕組みに与える影響

from my_package import func_a, ClassBが動作するのは、次のような手順で処理が行われているからです:

  1. from my_packageと書いた時点でmy_package__init__.pyが実行される。
  2. __init__.pyの中で、from .module_a import func_a, from .module_b import ClassBが実行されることで、func_aClassBは、my_packageパッケージの名前空間(属性)として公開されます。
  3. そのため、main.pyでは単にfrom my_package import func_a, ClassBと書くだけで、それらを直接importできるようになります。
スポンサーリンク

__all__ の使い方:import * を安全に制御する方法

__init__.pyのコードを書く際に重要になるのが、__all__という特殊変数です。__all__は 『from package import *を実行したときに、どのシンボル(関数・クラス・変数など)を公開するか』を制御します。

*(ワイルドカード)は「すべて」を意味しますが、実際にはパッケージ内のすべてのモジュールを自動的にインポートするわけではありません。

パッケージをインポートした際、__all__定義されていない場合import *は以下の要素をインポートします。

  1. __init__.pyファイル内で直接定義された変数、関数、クラス。
  2. __init__.pyファイル内でインポートされ、名前がアンダースコア (_) で始まらないもの。

このように__all__が定義されていないと、パッケージの設計者が意図しなかった中間的な変数や内部的な関数まで公開されてしまい、インポート元のコードの名前空間が汚染されたり、予期せぬ上書きによるバグにつながる危険性があります。

そこで、__all__というリスト変数を定義することで、import *を実行したときに、どの名前(シンボル)を外部に公開するかを明確に制御できます。これにより、意図しないインポートを防ぎ、パッケージのAPIとしての管理性コードの見通しが格段に向上します。

# __init__.py
from .module_a import func_a
from .module_b import ClassB

__all__ = ['func_a', 'ClassB']

これで、次のように「ワイルドカード import」を使っても、func_aClassBのみがインポートされます。

# main.py
from my_package import *

func_a()
obj = ClassB()

__init__.py に初期化処理を書く場合

__init__.pyのもう1つの使い方として、「パッケージが読み込まれたタイミングで実行される初期化処理」を記述するケースもあります。

例えば、fortune_cookiemessage/__init__.pyに、言語選択時のメッセージを登録することもできます。

# message/__init__.py
PROMPT_SELECT_LANGUAGE = '言語を選んでください (jp/en): '

fortune.pyからは次のように呼び出すことができます。

# fortune.py
import message

lang_code = input(message.PROMPT_SELECT_LANGUAGE)
print(lang_code)

__init__.py 使用時のベストプラクティスまとめ

最後に、パッケージ構造のベストプラクティスを整理しておきましょう。

  1. パッケージには必ず__init__.pyを置く。
  2. 内部構造を意識させずに使えるように、__init__.pyでよく使う関数・クラスをまとめてimportしておく。
  3. __all__を定義して、外部公開するAPIを明示的に制御する。
  4. 初期化処理は必要最低限にとどめ、基本はクリーンな構成を保つ。

これらを意識するだけで、パッケージ設計の見通しがぐっと良くなります。

スポンサーリンク