pythonのimportあれこれ
importでハマって丸一日つぶしたのでメモ。
今回の教材
feiz@dev % tree /home/feiz/tmp/imp/ |-- ddd.py |-- xxx | |-- __init__.py | |-- aaa.py | `-- eee.py `-- yyy |-- __init__.py |-- bbb.py `-- zzz |-- __init__.py `-- ccc.py
各__init__.pyにはprint __name__ + "is loaded"なる文を仕込んでます
init以外のモジュールにはクラスを一個だけ定義しています
ipythonを/home/feiz/tmp/imp/で起動しています
import と from 〜 importの違い
どういう名前でローカル名前空間に束縛するかだけ。
二重にロードされることもない
In [1]: import xxx.aaa xxxis loaded In [2]: from xxx import aaa In [3]: aaa Out[3]: <module 'xxx.aaa' from 'xxx/aaa.py'> In [4]: xxx.aaa Out[4]: <module 'xxx.aaa' from 'xxx/aaa.py'>
モジュールとパッケージ
パッケージをロードしただけではその中にあるパッケージやモジュールは見えません
In [1]: import xxx xxxis loaded In [2]: xxx.aaa --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) /home/feiz/tmp/imp/<ipython console> in <module>() AttributeError: 'module' object has no attribute 'aaa'
ただし、いずれかの場所で一度でもロードされていると上位のモジュールからも見えるようになります。
In [3]: from xxx import aaa In [4]: xxx.aaa Out[4]: <module 'xxx.aaa' from 'xxx/aaa.pyc'>
モジュールの中身は普通に見えます。
In [1]: from xxx import aaa xxxis loaded In [2]: aaa.Foo Out[2]: <class 'xxx.aaa.Foo'>
PYTHONPATH
モジュールの名前空間はPYTHONPATHごとに独立しているようです。
なので一つのモジュールに対して2通りのPYTHONPATHを通してしまうと非常にめんどくさいことになります。
feiz@debian14 % export PYTHONPATH=/home/feiz/tmp/imp/yyy/;ipython In [1]: from yyy import zzz yyyis loaded yyy.zzzis loaded In [2]: import zzz zzzis loaded
同じzzzパッケージをインポートしているように見えますが、検索パスが違うので別物としてロードされます。
結果、zzzのinitが2回走ってしまいます。
Djangoとかいうフレームワークではまさにこのようなことが行われており、回避がたいへん難しいバグを引き起こす場合があります。
やめましょう。
おしまい
今回はパッケージのロードだけでは下位モジュールは(普通は)見えないということを知らなかったためにやたらはまりました。
まあいい勉強になったのでよしとします。