Thu Sep 27 2018

Python の import について

import の仕組みについて真面目に知りたい.

以下のようなディレクトリ構成を考える.

.
+- aa
    +- __init__.py
    +- a.py
    +- b.py
+- bb.py

ファイルの中身は以下の通り. val という変数をただ宣言してるだけだが, aa/b.py だけ import をしている.

   cat aa/__init__.py
val = "init"

   cat aa/a.py
val = "aa/a"

   cat aa/b.py
import aa.a
import bb

val = "aa/b"

   cat bb.py
val = "bb"

REPLを立ち上げて以下を実行する.

In [1]: import aa.a

In [2]: aa.a.val
Out[2]: 'aa/a'

In [3]: aa.val
Out[3]: 'init'

In [4]: aa.b.val
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-6eae9e67d772> in <module>()
----> 1 aa.b.val

AttributeError: module 'aa' has no attribute 'b'

すなわち aa.aimport をすれば aa.a 以下に宣言されたものを見ることが出来る. また aa.*import すると上に遡って見つかる __init__.py も自動的に import されるため aa.val を見ることが出来る. aa.b は今は関係がないので見ることが出来ない.

次に REPL を再起動して次を実行する.

In [1]: import aa.b

In [2]: aa.b.val
Out[2]: 'aa/b'

In [3]: aa.val
Out[3]: 'init'

In [4]: aa.a.val
Out[4]: 'aa/a'

In [5]: bb.val
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-6-1d86821e907f> in <module>()
----> 1 bb.val

NameError: name 'bb' is not defined

今度は aa.bimport すれば、やはり aa.b 以下と aa.__init__ 以下が見える. 先ほどと事情が異なるのは aa.a は今回は REPL では import していないのに見えていること. 原因として考えられるのはもちろん、 aa/b.py の中で import aa.a としていることしかない. 中で import したものが REPL レベルにまで影響を及ぼすというのは過激な気がする. 更に混乱させるのは、同様に import bb もしていたのに関わらず、こちらは REPL からは見えないことだ.

予想: 同じパッケージの import はトップレベルにまで影響する.