Python を使い始めると、ディレクトリの階層で分けてファイルを管理したくなります。
そこで出てくるのが __init__.py
ファイル。
これは一体何者なのか。
色々と情報がころがってはいるものの、なかなか納得行くように説明しているものが見当たりません。
Python のドキュメントでも、何を参照すれば正解なのかがわかりにくい1。
ということで、__init__.py
についてまとめてみました。(少し長いです)
読み物形式で書いていますので、結論(「__init__.py
の役割」)だけ見たい方はスクロールして最後の方を読んでください。
python コードの例は、主に 3.6/3.5 を使用しています2。
- 「モジュール」と「パッケージ」と「名前空間」
- モジュールと階層構造
- 単一ファイルのモジュール
- ディレクトリによる階層構造と名前空間
- ディレクトリと名前空間のマッピング
-
__init__.py
の役割- モジュール検索のためのマーカー
- 名前空間の初期化
- ワイルドカード import の対象の定義 (
__all__
の定義) - 同じディレクトリにある他のモジュールの名前空間の定義
- まとめ
- 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.
import alpha.bravo.charlie
alpha.bravo.charlie.delta()
この例 (例1) では、alpha
→ bravo
→ charlie
という階層構造の名前空間になっており、その charlie
の中で実装された delta()
という手続き (関数) を呼び出しています。
上位の名前空間が異なれば、下位の名前空間で名前が同じであっても、異なるアイテムである、ということは直感的にわかると思います。
import os.path
os.path.join('/home', 'user')
こちら (例2) は、よく使われるパスの結合例で、os
→ path
という名前空間の join()
を呼び出しています。
モジュールと階層構造
単一ファイルのモジュール
同じディレクトリに二つのファイルを用意します。
./
├─ module01.py ..... モジュール
└─ sample0010.py ... 実行ファイル (= メインモジュール)
- モジュール1
module01.py
: "Hello, world!" を出力するだけの関数hello()
を定義しています。
def hello():
print( "Hello, world!" )
- 実行ファイル
sample0010.py
:import
でmodule01
を読み込み、module01.hello()
を呼び出しています。
import module01
module01.hello()
- 実行結果
$ python3 sample0010.py
Hello, world!
$ python2 sample0010.py
Hello, world!
単一ファイルのモジュールは、同じディレクトリに置くことで、import
することができます。
ファイルを分けるだけであれば、ディレクトリを作る必要はありません。
この時、__init__.py
は不要です。
ファイル名は module01.py
ですが、モジュール名は module01
(ファイル名から .py を除いたもの)です。
名前空間は、module01
です。
ディレクトリによる階層構造と名前空間
次のようなディレクトリ、ファイル構成を考えます。
./
├─ sample0020.py ..... 実行ファイル
└─ dir/
└─ module02.py ... モジュール
def hello():
print( "Hello, world! from module02" )
この時、module02
を呼び出すためには、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
として呼び出せるようになります。
./
├─ sample0030.py ..... 実行ファイル
└─ module02/
└─ __init__.py ... "module02" の実体
def hello():
print( "Hello, world! from __init__.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
の役割がわかりやすいと思います。
-
__init__.py
は、モジュール検索のためのマーカーとなる。 -
__init__.py
は、それが存在するディレクトリ名を名前とする名前空間の初期化を行う。 -
__init__.py
は、同、名前空間におけるワイルドカード import の対象を定義する (__all__
の定義) 。 -
__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 スクリプトファイルを用意します。
./
├─ sample0040.py ... 実行ファイル
└─ module04.py ..... モジュール
from module04 import *
hello1()
hello2()
hello3()
sample0040.py
では、from module04 import *
というように、*
を使用して import
しています。
import
後、hello1()
、 hello2()
、 hello3()
を順に呼び出すという簡単なプログラムです。
__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()
も呼び出し可能です。
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
には初期化のみを記述して、ファイルを外に出したくなってきます。
以下のようなディレクトリ、ファイル構成で試してみます。
./
├─ sample0050.py ...... 実行ファイル
└─ module05
├─ __init__.py .... "module05" の初期化ファイル
├─ _module05.py ... "module05" の実体
└─ module06.py .... "module05" の追加モジュール
module05/_module05.py
は、__init__.py
が膨れ上がったので、外に出した、あるいは、初めから module05
として提供するために開発した、という想定です。
モジュール名の実体としてわかるように、アンダースコア (_
) を付けて、ディレクトリと同じファイル名にしました。
print( "in _module05.py" )
def hello(caller=""):
print( "Hello, world! in _module05 called by {}".format(caller) )
module05/module06.py
は、最初から __init__.py
の外部で開発を進めたファイル、という想定です。
print( "in module06.py" )
def hello(caller=""):
print( "Hello, world! in module06 called by {}".format(caller) )
_module05.py
の hello()
も、module06.py
の hello()
も、呼び出し側が分かるように、caller を引数に渡すようにしてあります。
さて、__init__.py
ですが、同じディレクトリにあるモジュールを読み込むので、_module05
と module06
の頭に、カレントディレクトリ(同一名前空間)を表すドット (.
) を付けてあります。
また、hello()
の名前が衝突しているので、as
を用いて、名前を変更しています。
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
のモジュールのみを読み込んでいます。
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.py
と module05/module06.py
が、module05
として呼び出される様子がわかったかと思います。
ちなみに、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
の役割について、検証しながら確認しました。
- 階層化されたモジュールを import するためには
__init__.py
が必要。
(v3.3 で追加された__init__.py
を設置しない「Implicit Namespace Packages」については、ここでは触れません) -
__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 での階層下のテストモジュールの検索に使われています。
以下に実例を示しておきます。
./
├─ 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__.py
と my_module/_my_module.py
を設置。
__all__ = [
'Sample',
]
from ._my_module import Sample
#!/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
を置かない。
#!/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
を置く。
# nothing here
tests-without__init__/unit/test_my_module.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
がない場合は……
$ python3 -m unittest discover tests-without__init__/ -v
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
$
__init__.py
がないと、unit/
下のテストプログラムを見つけてくれず、テストが実行されません。
-
「Python チュートリアル - モジュール」を読むのが正解のようです(利用しているバージョンに合ったドキュメントを参照してください)。チュートリアルとして、最初に読んだときは、全然理解できず、記憶の彼方に飛んでいってしまいました。 ↩
-
雑音を少なくするために、
#!/usr/bin/env python
などの shebang や、文字コード指定の-*- coding: utf-8 -*-
などを排除して、コア部分のみをシンプルに記述しています。 ↩ -
「Python 一般の用語」には、「Python においてコードを再利用する際の基本単位: すなわち、他のコードから import されるひとかたまりのコード」と記載されています。 ↩
-
「Python ドキュメント - チュートリアル - パッケージ」では、「他のモジュールが入っているモジュール」と記載されています。 ↩
-
「モジュールについてもうすこし」参照 ↩