Python WTF: 同じモジュールが二つ?

ふーをインポート


import foo

違うパスでインポート


import sys,os
sys.path.insert(0, '..')
import adir.foo
ここで「同じモジュールを2回インポートするなよ!」と叱られてほしい。

モジュールスコープの変数は違ったインスタンス。これにケツを噛まれた。


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
これは別名が同じモジュールに解決しているな。