同じディレクトリ内の別のファイルから関数をインポートしたい。
通常、次のいずれかが機能します。
from .mymodule import myfunction
from mymodule import myfunction
...しかし、もう 1 つでは、次のエラーのいずれかが発生します。
ImportError: attempted relative import with no known parent package
ModuleNotFoundError: No module named 'mymodule'
SystemError: Parent module '' not loaded, cannot perform relative import
どうしてこれなの?
ベストアンサー1
残念ながら、このモジュールはパッケージ内に存在する必要があり、場合によってはスクリプトとして実行可能である必要もあります。これを実現する方法はありますか?
このようなレイアウトになるのはよくあることです...
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()
...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
これを実行するには、-m オプションを使用して、Python モジュール システム (ファイルシステムではなく) のパスを指定します...
python3 -m mypackage.myothermodule
...しかし、これはやや冗長であり、 のようなシェバン行とうまく混在しません#!/usr/bin/env python3
。
別の方法としては、相対インポートの使用を避けて、単に... を使用することです。
from mypackage.mymodule import as_int
いずれにしても、 の親から実行するmypackage
か、そのディレクトリを に追加する必要があります(どちらにしても、が sys.path にあるPYTHONPATH
ことが保証されます)。mypackage
モジュール検索パス)。または、すぐに動作させたい場合は、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
ちょっと面倒ですが、その理由の手がかりはEメールグイド・ヴァン・ロッサムという人物によって書かれたものです...
私はこれと、この仕組みをいじくり回す他の提案に対しては -1 です
__main__
。唯一の使用例は、モジュールのディレクトリ内にあるスクリプトを実行することのようですが、私はこれを常にアンチパターンだと思っています。私の考えを変えるには、そうではないと私を説得する必要があります。
パッケージ内でスクリプトを実行することがアンチパターンであるかどうかは主観的ですが、個人的には、カスタム wxPython ウィジェットがいくつか含まれているパッケージでは、wx.Frame
テスト目的で任意のソース ファイルのスクリプトを実行してそのウィジェットのみを表示できるので、非常に便利だと思います。