静かなる名辞

pythonとプログラミングのこと

2019/03/22:TechAcademyがteratailの質問・回答を盗用していた件
2019/03/26:TechAcademy盗用事件 公式発表と深まる疑念


【python】fromでimportしたときの対象モジュールの実行の流れ

 ふと「あれ、どうなってるんだっけ」と思うことがあったので実験してみる。

import_test.py

import time

print("aaa")
def hoge():
    print("hoge")

print("bbb")
def fuga():
    print("fuga")

for i in range(1, 4):
    print(i)
    time.sleep(1)

 普通に実行すると、

$ python import_test.py
aaa
bbb
1
2
3

 当然こうなる。1,2,3は1秒おきに出力される。

 pythonではモジュールをimportした場合、モジュール内のコードがすべて実行される。また、一度読み込まれたモジュールは特別なことをしなければリロードされない。

>>> import import_test
aaa
bbb
1
2
3
>>> from import_test import hoge
>>> 

 一度対話的インタプリタを落として再起動し、素の状態でfrom import_test import hogeとしても、ぜんぶ実行されるのだろうか?

>>> from import_test import hoge
aaa
bbb
1
2
3
>>> hoge()
hoge

 された。そしてこの状態でfrom import_test import fugaとしても、すでにimport_test.pyがまるごと実行されているため、もう一度aaaからprintされることはなかった。

 納得が行くような気も、ちょっと行かないような気もするけど、とりあえずドキュメントを見に行ってみた。すると興味深い記述があった。

import 文は 2 つの処理を連続して行っています; ある名前のモジュールを探し、その検索結果をローカルスコープの名前に束縛します。
5. インポートシステム — Python 3.6.5 ドキュメント

 要するに、

  1. モジュールのロードは原則最初の1回だけ。ロードの単位はモジュール(最も一般的なのは.pyのファイル)であり、fromを使おうと一部の関数だけロードする、といったことはできない。モジュール内はすべて実行される。
  2. 2回目以降では、1回目でロードされたモジュールオブジェクトの構成要素に対するimport元の名前空間での名前束縛が発生するだけ。

 と考えれば良い、ということかな。だとするとそれなりに納得はできる。

 なお、この様子はsys.modulesで確認できるとドキュメントに書いてあった。

>>> import sys
>>> "import_test" in sys.modules
False
>>> from import_test import hoge
aaa
bbb
1
2
3
>>> "import_test" in sys.modules
True
>>> sys.modules["import_test"]
<module 'import_test' from '/.../import_test.py'> # ディレクトリ名略
>>> sys.modules["import_test"].fuga()
fuga

 なるほど、こうやってまるごとimportされている訳だね。