Help us understand the problem. What is going on with this article?

Python の __init__.py とは何なのか

Python を使い始めると、ディレクトリの階層で分けてファイルを管理したくなります。
そこで出てくるのが __init__.py ファイル。

これは一体何者なのか。
色々と情報がころがってはいるものの、なかなか納得行くように説明しているものが見当たりません。
Python のドキュメントでも、何を参照すれば正解なのかがわかりにくい1

ということで、__init__.py についてまとめてみました。(少し長いです)
読み物形式で書いていますので、結論(「__init__.py の役割」)だけ見たい方はスクロールして最後の方を読んでください。
python コードの例は、主に 3.6/3.5 を使用しています2

  1. 「モジュール」と「パッケージ」と「名前空間」
  2. モジュールと階層構造
    1. 単一ファイルのモジュール
    2. ディレクトリによる階層構造と名前空間
    3. ディレクトリと名前空間のマッピング
  3. __init__.py の役割
    1. モジュール検索のためのマーカー
    2. 名前空間の初期化
    3. ワイルドカード import の対象の定義 (__all__ の定義)
    4. 同じディレクトリにある他のモジュールの名前空間の定義
  4. まとめ
  5. unittest についての注意事項 (@methane さんからのコメントにより追記)

「モジュール」と「パッケージ」と「名前空間」

本題に入る前に、「モジュール」と「パッケージ」、「名前空間」について簡単に。

  • 「モジュール (Module)」は、ファイル単位で分けてプログラムを記載したもの。
    わかりにくければ、一つの foo.py という Python プログラムファイルが一つの「モジュール」と考えればよいでしょう3(これを「pure Python モジュール」と呼ぶようです)。

Glossary: module
An object that serves as an organizational unit of Python code. Modules have a namespace containing arbitrary Python objects. Modules are loaded into Python by the process of importing.

参照: 「Python ドキュメント - チュートリアル- モジュール

  • 「パッケージ (Package)」は、複数のファイル、ディレクトリ構造からなり、ルールに従ってそれらをひと固まりにしたもの4

Glossary: package
A Python module which can contain submodules or recursively, subpackages. Technically, a package is a Python module with an __path__ attribute.

  • 「名前空間 (Namespace)」は、「モジュール」や「パッケージ」(およびその中)で 使用する「名前」が衝突しないように、使用する名前の空間を分けたもの。
    階層構造で構成される。

Glossary: namespace
The place where a variable is stored. Namespaces are implemented as dictionaries. There are the local, global and built-in namespaces as well as nested namespaces in objects (in methods). Namespaces support modularity by preventing naming conflicts. For instance, the functions builtins.open and os.open() are distinguished by their namespaces. Namespaces also aid readability and maintainability by making it clear which module implements a function. For instance, writing random.seed() or itertools.islice() makes it clear that those functions are implemented by the random and itertools modules, respectively.

名前空間の例1
import alpha.bravo.charlie

alpha.bravo.charlie.delta()

この例 (例1) では、alphabravocharlie という階層構造の名前空間になっており、その charlie の中で実装された delta() という手続き (関数) を呼び出しています。

上位の名前空間が異なれば、下位の名前空間で名前が同じであっても、異なるアイテムである、ということは直感的にわかると思います。

名前空間の例2
import os.path

os.path.join('/home', 'user')

こちら (例2) は、よく使われるパスの結合例で、ospath という名前空間の join() を呼び出しています。

モジュールと階層構造

単一ファイルのモジュール

同じディレクトリに二つのファイルを用意します。

ファイル構成
./
├─ module01.py ..... モジュール
└─ sample0010.py ... 実行ファイル (= メインモジュール)
  • モジュール1 module01.py: "Hello, world!" を出力するだけの関数 hello() を定義しています。
module1.py
def hello():
    print( "Hello, world!" )
  • 実行ファイル sample0010.py: importmodule01 を読み込み、module01.hello() を呼び出しています。
sample0010.py
import module01
module01.hello()
  • 実行結果
sample0010.pyの実行
$ python3 sample0010.py
Hello, world!
$ python2 sample0010.py
Hello, world!

単一ファイルのモジュールは、同じディレクトリに置くことで、import することができます。
ファイルを分けるだけであれば、ディレクトリを作る必要はありません。

この時、__init__.py は不要です。
ファイル名は module01.py ですが、モジュール名は module01 (ファイル名から .py を除いたもの)です。
名前空間は、module01 です。

ディレクトリによる階層構造と名前空間

次のようなディレクトリ、ファイル構成を考えます。

サンプル2 階層構造
./
├─ sample0020.py ..... 実行ファイル
└─ dir/
    └─ module02.py ... モジュール
dir/module02.py
def hello():
    print( "Hello, world! from module02" )

この時、module02 を呼び出すためには、sample0020.py では、次のように記述する必要があります。

sample0020.py
import dir.module02
dir.module02.hello()
実行結果
$ python3 sample0020.py
Hello, world! from module02

$ python2 sample0020.py
Traceback (most recent call last):
  File "sample0020.py", line 1, in <module>
    import dir.module02
ImportError: No module named dir.module02

python3 では期待通りに動きましたが、python2 ではエラーになってしまいました。
これは、dir 下に __init__.py が必要なためです。

v3.3 からは、呼び出すモジュールのあるディレクトリに __init__.py が無くても呼び出せるようになっていますが、「通常のパッケージ」では、__init__.py を置くことになっています。

What’s New In Python 3.3: PEP 420: Implicit Namespace Packages
Native support for package directories that don’t require __init__.py marker files and can automatically span multiple path segments (inspired by various third party approaches to namespace packages, as described in PEP 420)

これは、V3.3 で新しく追加された「名前空間パッケージ (Namespace Packages)」のための機能で、「通常のパッケージ (Regular packages)」では、__init__.py が必要です。

Regular packages
A regular package is typically implemented as a directory containing an init.py file.

「名前空間パッケージ (Namespace Packages)」を作成するのでなければ、モジュールを置くディレクトリには __init__.py を置くことが求められています。

それでは、__init__.py の意味について、少し見ていきましょう。

ディレクトリと名前空間のマッピング

サンプル2 では、module02 を呼び出すのに、dir/ というディレクトリがあるために、dir.module02 という名前空間で参照する必要がありました。
dir が邪魔ですね。実体がないのに、名前空間の階層として指定しなければなりません。

そこで登場するのが、__init__.py
ディレクトリ階層にした上で、直接、module02 という名前で呼び出す方法が __init__.py です。

dir/ の代わりに module02/ というディレクトリ名にすればよいのですが、呼び出すファイルは、結局 module02.なにがし.hello() とせざるを得ません。そこで、 __init__.py というファイルに特別な意味をもたせ、ディレクトリ名と同じ名前空間のモジュールとして扱えるようになっています。

つまり、dir/module02.py の代わりに module02/__init__.py の中にプログラムを書くことで、module02 として呼び出せるようになります。

サンプル3 __init__.pyを含む階層構造
./
├─ sample0030.py ..... 実行ファイル
└─ module02/
    └─ __init__.py ... "module02" の実体
module02/__init__.py
def hello():
    print( "Hello, world! from __init__.py" )
sample0030.py
import module02
module02.hello()
実行結果
$ python2 sample0030.py
Hello, world! from __init__.py

$ python3 sample0030.py
Hello, world! from __init__.py

歴史的な経緯はわかりません(調べてません)が、これが本来の __init__.py だったのではないかと思います。(筆者の憶測)
ファイルとして存在するモジュールのクラス(名前空間)では __init__() を記載する場所がありますが、ディレクトリとして存在する名前空間には __init__() を書く場所がありません。それをこの __init__.py というファイルで構成するようにしたものだと思われます。

そして、__init__.py が名前空間の存在を表すという位置づけから、__init__.py がモジュールのマーカーとして使われていたのでしょう。
そのため、モジュールとして読み込むファイルがあるディレクトリには、__init__.py がなければならない (= __init__.py があるディレクトリが明示的な名前空間の一部である)、という実装になっていたものと想像しています。

__init__.py を置いた場合には、ディレクトリ名と同じ名前空間の実体モジュールとして扱われています。
もう少し違う言い方をすれば、__init__.py は、ディレクトリ名をモジュール名(あるいは明示的な「名前空間」)としてマッピングするためのファイル、と言えます。(ディレクトリ名を名前空間としたときのコンストラクタの役割)

これが理解できると、今までなんだかよくわからなかった __init__.py が、少し身近になってくるのではないでしょうか。

__init__.py の役割

ここまでのことをわかった上で Python チュートリアルの「モジュール (module)」を読むと、__init__.py の役割がわかりやすいと思います。

  1. __init__.py は、モジュール検索のためのマーカーとなる。
  2. __init__.py は、それが存在するディレクトリ名を名前とする名前空間の初期化を行う。
  3. __init__.py は、同、名前空間におけるワイルドカード import の対象を定義する (__all__ の定義) 。
  4. __init__.py は、同じディレクトリにある他のモジュールの名前空間を定義する。

2. ~ 4. をひとまとめにして、「モジュールあるいはパッケージの初期化」ということもできますが、ここでは分けてみました。

1. モジュール検索のためのマーカー

__init__.py は、階層のモジュールを検索するためのマーカーとして利用されます。
ディレクトリで階層化させたモジュールを読み込ませるためには、__init__.py が存在している必要があります。
(__init__.py が不要となる「名前空間パッケージ (Namespace Packages)」については、ここでは触れません)

Regular packages
Python defines two types of packages, regular packages and namespace packages. Regular packages are traditional packages as they existed in Python 3.2 and earlier. A regular package is typically implemented as a directory containing an __init__.py file.

2. 名前空間の初期化

既に見てきた通り、ディレクトリ名を名前空間とするモジュールとして扱う際に、__init__.py には最初に実行しておくべき内容を登録しておきます。下位のモジュールの import であっても、上位名前空間としての初期化が行われた後に、下位モジュールが実行されます。
下位モジュールの実行(読み込み)前に何かしらの初期化が必要な場合には、下位モジュールを import する前に実施しなければならないことに注意が必要です。

3. ワイルドカード import の対象の定義 (__all__ の定義)

「Python チュートリアル - パッケージから * を import する」に記載されていますが、from my_module import * という呼び出し方をしたときに、import される対象を定義するのが __all__ リストです。

これは、__init__.py に限った話ではなく、すべてのモジュールで定義可能です。

下のサンプル4を見てください。同じディレクトリに二つの Python スクリプトファイルを用意します。

サンプル4
./
├─ sample0040.py ... 実行ファイル
└─ module04.py ..... モジュール
sample0040.py
from module04 import *

hello1()
hello2()
hello3()

sample0040.py では、from module04 import * というように、* を使用して import しています。
import 後、hello1()hello2()hello3() を順に呼び出すという簡単なプログラムです。

module04.py
__all__ = ['hello1', 'hello2']

def hello1():
    print( "Hello, this is hello1" )

def hello2():
    print( "Hello, this is hello2" )

def hello3():
    print( "Hello, this is hello3" )

module04.py の中では、hello1()hello2()hello3() を定義しています。
__all__ リストには、'hello1''hello2' のみを含め、'hello3' は含まれていません。

実行結果は下の通りです。

実行結果
$ python sample0040.py
Hello, this is hello1
Hello, this is hello2
Traceback (most recent call last):
  File "sample0040.py", line 5, in <module>
    hello3()
NameError: name 'hello3' is not defined

hello3() の呼び出しは未定義として "NameError: name 'hello3' is not defined" というエラーになってしまいました。__all__ のリストに無いためです。
これは、hello3() が隠蔽されているわけではなく、あくまでも import * としたときの動作です。
試しに、* を使わずに import し、module04 を明示的に呼べば、hello3() も呼び出し可能です。

sample0041.py
import module04

module04.hello1()
module04.hello2()
module04.hello3()
実行結果
$ python sample0041.py
Hello, this is hello1
Hello, this is hello2
Hello, this is hello3

__init__.py の中で __all__ を定義するのは、ディレクトリ名を名前空間とするモジュールを *import したときに参照可能とするオブジェクトを定義しているにすぎません。

4. 同じディレクトリにある他のモジュールの名前空間の定義

__init__.py の中に関数などを定義することで、ディレクトリ名と同じ名前のモジュールとして呼び出しが可能なことは上で書きました。
__init__.py が大きくなってくると、__init__.py には初期化のみを記述して、ファイルを外に出したくなってきます。

以下のようなディレクトリ、ファイル構成で試してみます。

サンプル5
./
├─ sample0050.py ...... 実行ファイル
└─ module05
    ├─ __init__.py .... "module05" の初期化ファイル
    ├─ _module05.py ... "module05" の実体
    └─ module06.py .... "module05" の追加モジュール

module05/_module05.py は、__init__.py が膨れ上がったので、外に出した、あるいは、初めから module05 として提供するために開発した、という想定です。
モジュール名の実体としてわかるように、アンダースコア (_) を付けて、ディレクトリと同じファイル名にしました。

./module05/_module05.py
print( "in _module05.py" )

def hello(caller=""):
    print( "Hello, world! in _module05 called by {}".format(caller) )

module05/module06.py は、最初から __init__.py の外部で開発を進めたファイル、という想定です。

./module05/module06.py
print( "in module06.py" )

def hello(caller=""):
    print( "Hello, world! in module06 called by {}".format(caller) )

_module05.pyhello() も、module06.pyhello() も、呼び出し側が分かるように、caller を引数に渡すようにしてあります。

さて、__init__.py ですが、同じディレクトリにあるモジュールを読み込むので、_module05module06 の頭に、カレントディレクトリ(同一名前空間)を表すドット (.) を付けてあります。
また、hello() の名前が衝突しているので、as を用いて、名前を変更しています。

./module05/__init__.py
print( "in __init__.py" )

# import _module05.hello() as hello05() in the same directory
from ._module05 import hello as hello05
# import module06.hello() as hello06() in the same directory
from .module06 import hello as hello06

__all__ = ['hello05', 'hello06']

# Do initialize something bellow
hello05("__init__.py")
hello06("__init__.py")

__all__* 呼び出し可能なオブジェクトの定義、# Do initialize something bellow の下は、何かしらの初期化を行っているという想定です。

大元呼び出しの sample0050.py は以下の通り。
from module05 import * で、module05 のモジュールのみを読み込んでいます。

./sample0050.py
print( "in {} 1".format( __file__ ) )

from module05 import *

print( "in {} 2".format( __file__ ) )
hello05(__file__)
hello06(__file__)

実行結果は以下の通り。

実行結果
$ python3 sample0050.py
in sample0050.py 1
in __init__.py
in _module05.py
in module06.py
Hello, world! in _module05 called by __init__.py
Hello, world! in module06 called by __init__.py
in sample0050.py 2
Hello, world! in _module05 called by sample0050.py
Hello, world! in module06 called by sample0050.py

__init__.py が介在することで、module05/_module05.pymodule05/module06.py が、module05 として呼び出される様子がわかったかと思います。

ちなみに、module05/module06.py は、隠蔽されたわけではないので、直接呼び出すことも可能です。

module05/module06.pyの直接呼出し
$ python3 -c "import module05.module06; module05.module06.hello('shell')"
in __init__.py
in _module05.py
in module06.py
Hello, world! in _module05 called by __init__.py
Hello, world! in module06 called by __init__.py
Hello, world! in module06 called by shell

ここまでわかると、「パッケージ」として再利用可能なモジュール開発が進められるようになると思います。

まとめ

__init__.py の役割について、検証しながら確認しました。

  1. 階層化されたモジュールを import するためには __init__.py が必要。
    (v3.3 で追加された__init__.py を設置しない「Implicit Namespace Packages」については、ここでは触れません)
  2. __init__.py にはモジュールの初期化処理を記載

という役割があると書かれていることが多いのですが、ここでは、2番目の役割を 3つに分けて記述しました。

2-1. 名前空間の初期化
2-2. ワイルドカード import の対象の定義 (__all__ の定義)
2-3. 同じディレクトリにある他のモジュールの名前空間の定義

3番目は、正しくは、同じディレクトリにはない他のモジュールを import して定義することも可能ですが、まずは、同じディレクトリにあるモジュールということで、記述した次第です。

なお、モジュールに記載された実行文については、「これらの実行文は、インポート文の中で 最初に モジュール名が見つかったときにだけ実行されます。」と記載されており5、何度 import を繰り返しても、1度しか実行されません。(importlib を用いて importlib.reload() した場合には、明示的に実行されます)

この投稿を読んでから、改めて 「Python チュートリアル - モジュール」を読み直すと、より理解が深まるのではないかと思います。

少しでもお役に立てば幸いです。

unittest についての注意事項

@methane さんからのコメントにより追記 (2020/01/20)

__init__.py には マーカーとしての役割があり、特に身近なところでは、 unittest での階層下のテストモジュールの検索に使われています。

以下に実例を示しておきます。

unittestのテスト環境ツリー
./
├─ my_module
│   ├─ __init__.py ............. my_module マッピング用
|   └─ _my_module.py ........... my_module の実体
|
├─ tests-without__init__/ ...... __init__.py なしのテスト用ディレクトリ
|   └─ unit/ ................... unit テスト用の階層
|       └─ test_my_module.py ... テストプログラム
|
└─ tests-with__init__/ ......... __init__.py ありのテスト用ディレクトリ
    └─ unit/ ................... unit テスト用の階層
        ├─ __init__.py ......... ファイルを置くのみ。中身は空。
        └─ test_my_module.py ... tests-without/unit/test_my_module.py へのシンボリックリンク

my_module を用意するために my_module/__init__.pymy_module/_my_module.py を設置。

my_module/__init__.py - my_moduleとして_my_module.pyを読み込む
__all__ = [
    'Sample',
    ]

from ._my_module import Sample
my_module/_my_module.py - my_moduleの実体を定義
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import six

class Sample:
    def __init__(self):
        self.message = "__file__: " + __file__ + ", __name__:" + __name__
        self.saved_message = self.message

    def get(self):  # 今回のテスト対象
        return self.message

if __name__ == "__main__":
    sample = Sample()
    six.print_( sample.get() )

unittest を使ってテストを行うために、階層下に test_my_module.py を準備。
tests-without__init__/unit/ 下には __init__.py を置かない。

tests-without__init__/unit/test_my_module.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, os

import unittest

# 環境変数 MY_MODULE_PATH を読み込み、sys.path へ設定
module_dir = os.getenv( 'MY_MODULE_PATH', default=os.getcwd() )
sys.path.append( module_dir )

from my_module import Sample

class myClass(unittest.TestCase):
    global module_dir

    def setUp(self):
        self.moduledir = os.path.join( module_dir, "my_module" )
        self.modulefilepath = os.path.join( self.moduledir, "_my_module.py" )
        self.modulename = "my_module._my_module"
        self.sample = Sample()

    def tearDown(self):
        del self.sample

    # Sample.get() のテスト
    def test_get(self):
        self.assertEqual( self.sample.get(), "__file__: " + self.modulefilepath + ", __name__:" + self.modulename )

if __name__ == "__main__":
    unittest.main()

unittest を使ってテストを行うために、階層下に test_my_module.py を準備。
(実際には、シンボリックリンクで、tests-without__init__/unit/test_my_module.py の内容と同じ)
tests-with__init__/unit/ 下には空のファイル __init__.py を置く。

tests-with__init__/unit/__init__.py
# nothing here
tests-with__init__/unit/test_my_module.py
tests-without__init__/unit/test_my_module.py と同じ内容 (シンボリックリンク)

テストの実行。
まずは、__init__.py ありの方から。

__init__.pyあり実行結果
$ python3 -m unittest discover tests-with__init__/ -v
test_get (unit.test_my_module.myClass) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
$

Ran 1 test となっていて、unit/ 下のテストプログラムを見つけて、実行されました。

一方、__init__.py がない場合は……

__init__.pyなし実行結果
$ python3 -m unittest discover tests-without__init__/ -v

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK
$

__init__.py がないと、unit/ 下のテストプログラムを見つけてくれず、テストが実行されません。


  1. Python チュートリアル - モジュール」を読むのが正解のようです(利用しているバージョンに合ったドキュメントを参照してください)。チュートリアルとして、最初に読んだときは、全然理解できず、記憶の彼方に飛んでいってしまいました。 

  2. 雑音を少なくするために、#!/usr/bin/env python などの shebang や、文字コード指定の -*- coding: utf-8 -*- などを排除して、コア部分のみをシンプルに記述しています。 

  3. Python 一般の用語」には、「Python においてコードを再利用する際の基本単位: すなわち、他のコードから import されるひとかたまりのコード」と記載されています。 

  4. 「Python ドキュメント - チュートリアル - パッケージ」では、「他のモジュールが入っているモジュール」と記載されています。 

  5. モジュールについてもうすこし」参照 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした