Python WTF: 同じモジュールが二つ?
ふーをインポート
import foo
違うパスでインポート
ここで「同じモジュールを2回インポートするなよ!」と叱られてほしい。
import sys,os
sys.path.insert(0, '..')
import adir.foo
モジュールスコープの変数は違ったインスタンス。これにケツを噛まれた。
print foo.module_scope_var
print adir.foo.module_scope_var
確かに違う
print adir.foo.module_scope_var==foo.module_scope_var
False
それはモジュール自体が違うから。
print adir.foo==foo
False
モジュールをプリントしてみると
print adir.foo
print foo
やはり違う。
ファイルは?
print foo.__file__
print adir.foo.__file__
/home/tengu/python/wtf/adir/foo.pyc
../adir/foo.pyc
同じファイルなのに…
print os.path.abspath(foo.__file__)==os.path.abspath(adir.foo.__file__)
True
つまり
pythonは同じファイルを違うパスでインポートすると複数のモジュールを使うことになる。 モジュールファイルが同一でもそのレベルで統一してくれない。 モジュールはインポートネーム毎に管理される。 他の言語だとこういう場合、重複インポートとして叱られる気がする。
「同じモジュールを違う名前でインポートする奴があるか!」と思うかもしれないけど、 フレームワーク的なコードを大きなプロジェクトで使っていると、知らずとこうなることがある。 例えば、djangoプロジェクト下に便利な汎用モジュールがあったとする:
- blog/util.py
これをviewsが同じdirectoryなんでこのようにインポート
- blog/views.py: import util
別のappからはこのようにインポート
- link/views.py: import blog.util
utilに共有したい変数があったらwtfになってしまう。
つまり、インポートの仕方は画一にやった方がいいってことかな。
重複インポート発見方
あるいはこのようにチェックするか。
path_modules={}
for m in sys.modules.values():
if hasattr(m,'__file__'):
path_modules.setdefault(os.path.abspath(m.__file__),[]).append(m)
for path, modules in path_modules.items():
if len(modules)>1:
print 'dup:', path
for m in modules:
print m
dup: /home/tengu/python/wtf/adir/foo.pyc
dup: /usr/lib/python2.6/posixpath.pyc
あれ、posixpathもか?
これは別名が同じモジュールに解決しているな。
import os
import posixpath
>>> posixpath
>>> os.path
>>> posixpath==os.path
True