pythonのimportあれこれ

importでハマって丸一日つぶしたのでメモ。

基本的なことがら

Pythonのモジュールインポートのしくみ

6.12 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とかいうフレームワークではまさにこのようなことが行われており、回避がたいへん難しいバグを引き起こす場合があります。

やめましょう。

おしまい

今回はパッケージのロードだけでは下位モジュールは(普通は)見えないということを知らなかったためにやたらはまりました。

まあいい勉強になったのでよしとします。