Help us understand the problem. What is going on with this article?

[Python] importの躓きどころ

More than 3 years have passed since last update.

Pythonのimportに関しての個人的メモ。
間違っている点や他の躓きどころがあればコメント・PRをお願いします。

package内のimportにimplicit relative importは使えない

Python3以降の話。

例えば

$ tree .
.
└── mypackage
    ├── __init__.py
    ├── mymodule1.py
    └── mymodule2.py

というディレクトリ構造のときに、mymodule2.pyからmymodule1.pyをimportしたい。

./mypackage/mymodule1.py
A = 1000

def show():
    print('A: {}'.format(A))
./mypackage/mymodule2.py
import mymodule1

B = 100

def show():
    print('A: {}, B: {}'.format(mymodule1.A, B))

のとき、

$ python2.7
Python 2.7.10 (default, Oct 23 2015, 19:19:21)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import mypackage.mymodule2 as mymod2
>>> mymod2.show()
A: 1000, B: 100
$ python3
Python 3.5.2 (default, Aug  4 2016, 09:38:15)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import mypackage.mymodule2 as mymod2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/yusuke-nishioka/tests/tests1/mypackage/mymodule2.py", line 3, in <module>
    import mymodule1
ImportError: No module named 'mymodule1'

のように、Python2.7ではmymodule2.pyからmymodule1.pyをimportできているが、
Python3.5ではimportできていないことがわかる。

原因

mymodule2.py内のimport mymodule1
同階層のmymodule1.pyをimportすることを意図している(これをimplicit relative importと呼ぶ)。
しかし、sys.pathに同名のpackageがあった場合、
mypackage/mymodule1.pyをimportしようとしているのか
sys.path上のmymoduleというpackageをimportしようとしているのか
区別ができないという問題がある。

そのため、Python3系ではimport xxxsys.pathに存在するpackageをimportするように変更され(absolute importと呼ぶ)、
implicit relative importは不可能になった。

解決策

package内のモジュールをimportするためには、
from . import mymodule1のようにrelative importであることを明示しなければならない(これをexplict relative importと呼ぶ)。

参考URL

https://www.python.org/dev/peps/pep-0328/#rationale-for-relative-imports

__package__属性がNoneかつ__name__属性が__main__のとき、relative importは使えない

さて、上でpackage内のモジュールをimportするにはexplicit relavtive importを使うと述べたが、
package内のモジュールにも関わらずrelative importが使えない場合が存在する。
それはpackage内のモジュールの__package__属性がNoneかつ__name__属性が__main__のときである。

$ tree .
.
└── mypackage
    ├── __init__.py
    ├── main.py
    └── mymodule.py
mypackge/mymodule.py
A = 1000

def show():
    print('A: {}'.format(A))
mypackage/main.py
from . import mymodule

if __name__ == '__main__':
    print('__package__: {}, __name__: {}'.format(
        __package__, __name__))
    mymodule.show()

のとき、main.pyを実行すると

$ python3 mypackage/main.py
Traceback (most recent call last):
  File "mypackage/main.py", line 1, in <module>
    from . import mymodule
ImportError: cannot import name 'mymodule'

と表示され、mypackage/mymodule.pyをimportできていないことが分かる。

原因

このとき、main.pyの__package属性はNone、__name__属性は__main__となり、
top-levelのmoduleとして認識されている。
そのため、from ..はmain.py自身となりmymoduleをimportできない。

解決策1

from . import mymodulefrom mymodule import showとする。

解決策2

-mオプションをつけて実行する。

$ python3 -m mypackage.main
__package__: mypackage, __name__: __main__
A: 1000

__package属性はmypackage__name__属性は__main__となり
from ..はmain.pyと同じ階層を指すことになり、
from . import mymoduleによってmymodule.pyをimportすることができる。

ちなみに、-mによるmain.pyの実行はpackage外からおこなわれなければならない。

$ cd mypackage
$ python3 -m main
Traceback (most recent call last):
  File "/Users/yusuke-nishioka/.anyenv/envs/pyenv/versions/3.6.1/lib/python3.6/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/Users/yusuke-nishioka/.anyenv/envs/pyenv/versions/3.6.1/lib/python3.6/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/Users/yusuke-nishioka/Documents/United/tmp/mypackage/main.py", line 1, in <module>
    from . import mymodule
ImportError: attempted relative import with no known parent package

参考URL

https://www.python.org/dev/peps/pep-0328/#relative-imports-and-name

__init__.pyがなくてもpackageをimportできる

Python3.3以降の話。

$ tree
.
├── mypackage1
│   ├── __init__.py
│   └── subdir1
│       ├── __init__.py.bak
│       └── mymodule1.py
└── mypackage2
    └── subdir1
        └── mymodule2.py

のとき、

$ python3
Python 3.5.2 (default, Aug  4 2016, 09:38:15)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import mypackage1
>>> import mypackage2
>>> dir(mypackage1)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']
>>> dir(mypackage2)
['__doc__', '__loader__', '__name__', '__package__', '__path__', '__spec__']

のように、__init__.pyがなくてもimportできている。
__init__.pyがあるmypackage1をregular package、
__init__.pyがないmypackage2をnamespace packageと呼ぶ。

さらに、

>>> import sys
>>> sys.path.append('./mypackage1')
>>> sys.path.append('./mypackage2')
>>> import subdir1
>>> dir(subdir1)
['__doc__', '__loader__', '__name__', '__package__', '__path__', '__spec__']
>>> subdir1.__path__
_NamespacePath(['./mypackage1/subdir1', './mypackage2/subdir1'])

として、違うパスだが同じ名前のディレクトリも同じnamespaceに属するpackageとして
_NamespacePathオブジェクトに格納されている。

importの順序

import xxxが実行されると、

  • sys.pathxxx/__init__.pyが存在する場合、regular packageとして取得できる
  • sys.pathxxx/__init__.pyは存在しないがxxx.{py,pyc,so}が存在する場合、moduleとして取得できる
  • sys.pathxxx/__init__.pyxxx.{py,pyc,so}も存在しないが同名のディレクトリが存在する場合、namespace packageとして取得できる

regular packageとnamespace packageの違い

違いとして、

  • namespace packageには__file__属性がない
  • regular packageの__path__はリストだが、namespace packageの__path__は_NamespacePathオブジェクトである。

などがある。

さらに、namespace packageにする(__init__.pyをなくす)ことで、

>>> import subdir1.mymodule1
>>> import subdir1.mymodule2
>>> subdir1.mymodule1.__file__
'./mypackage1/subdir1/mymodule1.py'
>>> subdir1.mymodule2.__file__
'./mypackage2/subdir1/mymodule2.py'

のように、./mypackage1/subdir1以下のmymodule1.py
./mypackage2/subdir1以下のmymodule2.pyとを区別してimportできる。

まとめ

namespace packageを意図して使う場面は思いつかないが、
うっかり__init__.pyを忘れたとしてもmoduleとしてimportできる。
が、importの際に初期化処理を実行できる(__all__を設定するなど)ことを考えると、
特に理由がない限り__init__.pyを置くのが良いと思われる。

詳しくはPEP 420 -- Implicit Namespace Packagesを参照。

参考URL

PEP 420 -- Implicit Namespace Packages
2012/07/24 Python 3.3b1 の名前空間パッケージを試してみた

ysk24ok
今後Qiitaに記事を更新することはありません。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away