web-dev-qa-db-ja.com

Python:インポートされたパッケージからモジュールをインポートしようとすると 'ModuleNotFoundError'

Python 3.7.1をmacOS Mojaveバージョン10.14.1で使用しています。

これは私のディレクトリ構造です:

man/                          
  Mans/                  
          man1.py
  MansTest/
          SoftLib/
                  Soft/
                      SoftWork/
                              manModules.py
          Unittests/
                    man1test.py

man1.pyには、次のimportステートメントが含まれています変更したくない

from Soft.SoftWork.manModules import *

man1test.pyには、次のimportステートメントが含まれています。

from ...MansTest.SoftLib import Soft
from ...Mans import man1

man1test.pyman1test.pyの関数にアクセスする必要があるため、man1.pyの2番目のimportが必要です。

最初のインポート(Soft)の背後にある私の理論的根拠は、前述のman1.pyimportステートメントを容易にすることでした。

しかし、私の期待に反して、man1.pyimportステートメントは次のようになります。

ModuleNotFoundError: No module named 'Soft'

私が走るとき

python3 -m man.MansTest.Unittests.man1test

man /の上のディレクトリから。

man1.pyandimportステートメントを変更せずにこのエラーを解決する方法はありますかに何も追加しませんsys.path

編集:元の質問のバージョンからpython3 -m man.ManTest.Unittests.man1testpython3 -m man.MansTest.Unittests.man1testに変更されました

3
strangeloop

セットアップにはいくつかの混乱する要件がありますが、私はあなたが望むものをあなたに与えるように努めます。

[〜#〜]最初[〜#〜]man1.pyfromman1test.pyANDmanModules.pyfromman1.py、ファイルを パッケージとモジュール として適切に設定する必要があります。

パッケージは、「ドット付きモジュール名」を使用してPythonのモジュール名前空間を構造化する方法です。たとえば、モジュール名A.Bは、Bというパッケージ内のAというサブモジュールを示します。

...

パッケージをインポートするとき、Pythonはsys.path上のディレクトリを検索して、パッケージのサブディレクトリを探します。

__init__.pyファイルは、Pythonをディレクトリをパッケージを含むものとして扱うために必要です。これは、stringなどの一般的な名前のディレクトリが意図せずに行われないようにするために行われますモジュール検索パスで後で発生する有効なモジュールを非表示にします。

次のように設定する必要があります。

man
|- __init__.py
|- Mans
   |- __init__.py
   |- man1.py
|- MansTest
   |- __init.__.py
   |- SoftLib
      |- Soft
         |- __init__.py
         |- SoftWork
            |- __init__.py
            |- manModules.py
      |- Unittests
         |- __init__.py
         |- man1test.py

[〜#〜] second [〜#〜]、(ModuleNotFoundError: No module named 'Soft'で発生した "from ...Mans import man1"エラーの場合man1test.py、それに対する文書化された解決策は、man1.pysys.pathに追加することですMansMansTestパッケージの外にあります。 Pythonドキュメントから モジュール検索パス を参照してください。ただし、sys.pathを直接変更したくない場合は、PYTHONPATH

sys.pathは次の場所から初期化されます:

  • 入力スクリプトを含むディレクトリ(またはファイルが指定されていない場合は現在のディレクトリ)。
  • PYTHONPATH(シェル変数PATHと同じ構文のディレクトリ名のリスト)。
  • インストールに依存するデフォルト。

[〜#〜] 3番目[〜#〜]、あなたが言ったfrom ...MansTest.SoftLib import Softのために、「が容易になりました前述のman1.pyのインポートステートメントで、インポートが機能します。 Soft.SoftLibman1.pyにインポートする場合は、man1をセットアップする必要があります。 pyを検索してSoft.SoftLibを見つけ、そこに直接インポートします。

そうは言っても、ここで私はそれを機能させました。

man1.py:

from Soft.SoftWork.manModules import *
# no change to import statement but need to add Soft to PYTHONPATH

def foo():
    print("called foo in man1.py")
    print("foo call module1 from manModules: " + module1())

man1test.py

# no need for "from ...MansTest.SoftLib import Soft" to facilitate importing..
from ...Mans import man1

man1.foo()

manModules.py

def module1():
    return "module1 in manModules"

端末出力:

$ python3 -m man.MansTest.Unittests.man1test
Traceback (most recent call last):
  ...
    from ...Mans import man1
  File "/temp/man/Mans/man1.py", line 2, in <module>
    from Soft.SoftWork.manModules import *
ModuleNotFoundError: No module named 'Soft'
$ PYTHONPATH=$PYTHONPATH:/temp/man/MansTest/SoftLib
$ export PYTHONPATH
$ echo $PYTHONPATH
:/temp/man/MansTest/SoftLib
$ python3 -m man.MansTest.Unittests.man1test
called foo in man1.py
foo called module1 from manModules: module1 in manModules 

提案として、おそらくそれらのSoftLibファイルの目的を再考してください。 man1.pyman1test.pyの間の「ブリッジ」のようなものですか?現在のファイルのセットアップ方法ですが、期待どおりに機能するとは思いません。また、テスト対象コード(man1.py)がテストフォルダー(MansTest)。

8
Gino Mempin