1. ホーム
  2. パイソン

[解決済み】Python 3での相対インポート

2022-03-23 18:30:16

質問

同じディレクトリにある別のファイルから関数をインポートしたいのですが、どうすればいいですか?

でうまくいくこともあります。 from .mymodule import myfunction が、時々出る。

SystemError: Parent module '' not loaded, cannot perform relative import

で動作することもあります。 from mymodule import myfunction を取得することもあります。

SystemError: Parent module '' not loaded, cannot perform relative import

このロジックは理解できませんし、説明も見つかりませんでした。これは完全にランダムに見えます。

どなたか、どういう理屈か説明してください。

どのように解決するのですか?

<ブロッククオート

残念ながら、このモジュールはパッケージの中にある必要があり、また、このモジュールは はスクリプトとして実行可能である必要があります、時々。どのようにしたら を実現できますか?

こんなレイアウトはよくありますよね...。

main.py
mypackage/
    __init__.py
    mymodule.py
    myothermodule.py

...を使って mymodule.py このように...

#!/usr/bin/env python3

# Exported function
def as_int(a):
    return int(a)

# Test function for module  
def _test():
    assert as_int('1') == 1

if __name__ == '__main__':
    _test()

...a myothermodule.py このように...

#!/usr/bin/env python3

from .mymodule import as_int

# Exported function
def add(a, b):
    return as_int(a) + as_int(b)

# Test function for module  
def _test():
    assert add('1', '1') == 2

if __name__ == '__main__':
    _test()

...そして main.py こんな感じで...

#!/usr/bin/env python3

from mypackage.myothermodule import add

def main():
    print(add('1', '1'))

if __name__ == '__main__':
    main()

を実行すると、正常に動作します。 main.py または mypackage/mymodule.py を指定すると失敗します。 mypackage/myothermodule.py 相対インポートのため...

from .mymodule import as_int

実行の仕方は

python3 -m mypackage.myothermodule

...しかし、これはやや冗長で、次のような shebang 行とうまく組み合わせられません。 #!/usr/bin/env python3 .

この場合の最も単純な修正方法は、名前を想定して mymodule がグローバルに一意である場合、相対インポートの使用を避け、単に...を使用することです。

from mymodule import as_int

...ただし、一意でない場合、あるいはパッケージの構造がより複雑な場合は、パッケージディレクトリを含むディレクトリを PYTHONPATH と、こんな感じです。

from mypackage.mymodule import as_int

また、「箱から出してすぐに使える」ようにしたい場合は PYTHONPATH を、まずこのようにコードで記述します...

import sys
import os

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(SCRIPT_DIR))

from mypackage.mymodule import as_int

ちょっと面倒ですが、その理由のヒントは 電子メール Guido van Rossum氏によって書かれた...

私は、この件と、その他に提案されている __main__ を使用します。唯一のユースケースは、たまたま実行したスクリプトが モジュールのディレクトリの中に住んでいること。 アンチパターン 私の考えを変えさせるには、以下のことを納得させなければなりません。 ということです。

パッケージ内でスクリプトを実行することがアンチパターンであるかどうかは主観的なものですが、個人的には、いくつかのカスタムwxPythonウィジェットを含むパッケージでとても便利だと感じています。 wx.Frame テスト用にそのウィジェットだけを含んでいます。