__init__.pyでも「非パッケージでの相対インポートの試み」を修正する方法


744

次のディレクトリ構造で、PEP 328をフォローしようとしています。

pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py

core_test.py、私は、次のimport文を持っています

from ..components.core import GameLoopEvents

しかし、実行すると次のエラーが発生します。

tests$ python core_test.py 
Traceback (most recent call last):
  File "core_test.py", line 3, in <module>
    from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package

周りを検索すると、「__ init__.pyでも相対パスが機能しない」と「相対パスからモジュールをインポートする」が見つかりましたが、役に立ちませんでした。

ここで見逃しているものはありますか?


17
またunittest、プロジェクトを構造化するさまざまな方法にも非常に混乱していたため、モジュールの深い入れ子、相対インポートと絶対インポート(作業と非インポート)、および内部からの相対参照と絶対参照をカバーするこのかなり徹底的なサンプルプロジェクトを書きました。パッケージ、およびクラスのシングル、ダブル、およびパッケージレベルのインポート。私にとって正しいことを明確にするのに役立ちました!
cod3monk3y 2014

1
テストを機能させることができませんでした。no module named myimports.foo私がそれらを実行するときに取得し続けます。
Blairg23、2015年

@ Blairg23私が意図した呼び出しがあるに推測しているcdPyImports、実行python -m unittest tests.test_absたとえば、。
duozmo 2016年

7
私はジーンに同意します。もう少し役立つインポートプロセスをデバッグするためのメカニズムがあればいいのにと思います。私の場合、同じディレクトリに2つのファイルがあります。1つのファイルを別のファイルにインポートしようとしています。私が持っている場合はINIT非パッケージのエラーで試み相対インポート:そのディレクトリ内の.pyファイルを、私はとValueErrorを取得します。init .pyファイルを削除すると、「NAME」という名前のモジュールがないというエラーが発生します。
user1928764 2017年

私の場合、同じディレクトリに2つのファイルがあります。1つのファイルを別のファイルにインポートしようとしています。私が持っている場合はINIT非パッケージのエラーで試み相対インポート:そのディレクトリ内の.pyファイルを、私はとValueErrorを取得します。init .pyファイルを削除すると、「NAME」という名前のモジュールがないというエラーが発生します。本当にイライラしているのは、これが機能していて、PYTHONPATHが何かに設定されている.bashrcファイルを削除することで自分の足を撃ち、現在は機能していないことです。
user1928764 2017年

回答:


443

はい。パッケージとして使用していません。

python -m pkg.tests.core_test

51
落とし穴:最後に「.py」がないことに注意してください!
マインドシーフ

497
私はどちらか一方の投票者ではありませんが、この質問と回答の人気を考えると、これはかなり詳細に使用できると思います。上記のシェルコマンドを実行するためのディレクトリ__init__.pyなどの__package__情報に注意してください。最後まで実行する必要があるという事実、および実行可能なスクリプトのインポートを許可するために(たとえば、shebangとやって./my_script.py)Unixシェルでは、すべての有用であろう。この問題全体は、簡潔で理解しやすいドキュメントを理解したり見つけたりするのが非常に困難でした。
マークアメリー2014年

16
注:pkgCLIからこの行を呼び出すときは、ディレクトリの外にいる必要があります。その後、期待どおりに動作するはずです。中pkgにいるときにを呼び出すとpython -m tests.core_test、機能しません。少なくとも、それは私には向いていませんでした。
Blairg23、2015年

94
真剣に、あなたの答えで起こっていることを説明できますか?
ピノキオ2016年

18
@MarkAmeryこれがどのように機能するかを理解しようとする私の心をほとんど失いました。ファイルを含むpyファイルを含むサブディレクトリを持つプロジェクト内の相対インポートで__init__.pyValueError: Attempted relative import in non-packageエラーが発生し続けます。私はどこかで誰かに本当に良いお金を払って、これらすべてがどのように機能するかを最終的にプレーンな英語で説明します。
AdjunctProfessorFalcon

635

イグナシオバスケスエイブラムスの回答について詳しく説明するには:

Pythonインポートメカニズム__name__は、現在のファイルのに関連して機能します。ファイルを直接実行すると、そのファイルには通常の名前はありませんが、"__main__"代わりに名前として使用されます。したがって、相対インポートは機能しません。

Igancioが提案したように、-mオプションを使用して実行できます。スクリプトとして実行することを意図したパッケージの一部がある場合は、__package__属性を使用して、パッケージ階層でのファイルの名前をそのファイルに指示することもできます。

詳細については、http://www.python.org/dev/peps/pep-0366/を参照してください。


55
サブディレクトリpython -m core_test内からは実行できないことに気づくまでしばらく時間がかかりましたtests。親からのものであるか、親をパスに追加する必要があります。
Aram Kocharyan 2013

3
@DannyStaple:正確ではありません。を使用__package__して、実行可能スクリプトファイルが同じパッケージ内から他のモジュールを比較的インポートできるようにすることができます。「システム全体」から比較的インポートする方法はありません。なぜあなたがこれをしたいのか、私にもわかりません。
BrenBarn 2013

2
つまり、__package__シンボルが「parent.child」に設定されている場合は、「parent.other_child」をインポートできます。たぶん私はそれをそれほどうまく言い表していない。
ダニーステープル2013

5
@DannyStaple:まあ、それがどのように機能するかは、リンクされたドキュメントで説明されています。script.pyパッケージpack.subpackにスクリプトがある場合、それをに設定__package__するpack.subpackと、from ..module import somethingから何かをインポートできますpack.module。ドキュメンテーションが言うように、あなたはまだシステムパスにトップレベルのパッケージを持っている必要があることに注意してください。これはすでに、インポートされたモジュールの動作方法です。唯一のこと__package__は、その動作を直接実行されるスクリプトにも使用できるようにすることです。
BrenBarn 2013

3
__package__直接実行されるスクリプトで使用していますが、残念ながら次のエラーが表示されます。「親モジュール 'xxx'がロードされていないため、相対インポートを実行できません。」
mononoke

202

import components.core現在のディレクトリをsys.path次のように追加すると、直接使用できます。

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))

35
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))これも機能します
アジャイ2013年

26
from os import sys浮気のように見えます:)
空飛ぶ羊

3
@Piotr:それは少しのショーので、それは何がに追加されているより明確に、より良いと考えられるかもしれないですsys.path-現在のファイルが入っているディレクトリの親。
マーティ

8
@flyingsheep:そうですね、私は通常のを使用しimport sys, os.path as pathます。
martineau 2014年

10
参考までに、これをipythonノートブックで使用するために、この回答を次のように適応させましたimport os; os.sys.path.append(os.path.dirname(os.path.abspath('.')))。次に、ストレートがimport components.core機能し、必要に応じてノートブックの親ディレクトリからインポートします。
2014年

195

スクリプトをどのように起動するかによって異なります。

UnitTestをコマンドラインから従来の方法で起動する場合は、次のようになります

python tests/core_test.py

そこで、この場合、以来、「コンポーネント」「テストは」兄弟フォルダです、あなたはいずれかを使用して相対的なモジュールをインポートすることができ、挿入アペンドの方法のsys.pathのモジュールを。何かのようなもの:

import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents

それ以外の場合は、「-m」引数を使用してスクリプトを起動できます(この場合、ここではパッケージについて話しているため、「。py」拡張子を付けないでください)。

python -m pkg.tests.core_test

このような場合、単純に相対インポートを使用できます。

from ..components.core import GameLoopEvents

最終的に2つのアプローチを組み合わせることができるため、スクリプトはどのように呼び出されても機能します。例えば:

if __name__ == '__main__':
    if __package__ is None:
        import sys
        from os import path
        sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
        from components.core import GameLoopEvents
    else:
        from ..components.core import GameLoopEvents

3
デバッグにpdbを使用しようとしている場合はどうすればよいですか?python -m pdb myscript.pyデバッグセッションの起動に使用するため。
ダニー2015

1
@dannynjust-2つのメインモジュールを持つことができないので、これは良い質問です。一般に、デバッグするときは、デバッグを開始する最初のポイントから手動でデバッガーにドロップすることを好みます。これを行うimport pdb; pdb.set_trace()には、コードにインラインを挿入します(インライン)。
mgilson

3
insert代わりに使用した方が良いappendですか?つまり、sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
SparkAndShine 2016年

2
挿入の使用は、インストールされたパッケージよりもローカルパッケージ名が優先される、相対的なインポートセマンティクスに適しています。特にテストの場合、通常はインストールされたバージョンではなくローカルバージョンをテストします(テストインフラストラクチャがテスト対象のコードをインストールする場合を除きます。この場合、相対インポートは不要であり、この問題は発生しません)。
Alex Dupuy

1
また、モジュールとして実行するときは、core_testを含むディレクトリにいることはできない(それは簡単すぎるでしょう)
Joseph Garvin


10

ユースケースがテストの実行であり、それがシームである場合、次のことができます。python core_test.pyなどのテストフレームワークを使用して、テストスクリプトを実行する代わりにpytest。次に、コマンドラインで次のように入力できます

$$ py.test

これにより、ディレクトリでテストが実行されます。これは、@ BrenBarnによって指摘された__name__存在の問題を__main__回避します。次に、空の__init__.pyファイルをテストディレクトリに配置します。これにより、テストディレクトリがパッケージの一部になります。その後、あなたはできるようになります

from ..components.core import GameLoopEvents

ただし、テストスクリプトをメインプログラムとして実行すると、もう一度失敗します。したがって、テストランナーを使用してください。たぶん、これは他のテストランナーなどでも機能しnosetestsますが、確認していません。お役に立てれば。


9

私の簡単な修正は、ディレクトリをパスに追加することです:

import sys
sys.path.insert(0, '../components/')

6
'../'の部分は、スクリプトを実行するディレクトリ(core_test.py)から解決されるため、すべてのケースでアプローチが機能するわけではありません。あなたのアプローチでは、core_test.py scritpを実行する前に「テスト」にcdする必要があります。
xyman 2016

7

問題はテスト方法にあり、

あなたがしようとした python core_test.py

次に、このエラーが表示されます ValueError:非パッケージでの相対インポートが試行されました

理由:非パッケージソースからパッケージをテストしています。

パッケージのソースからモジュールをテストしてください。

これがあなたのプロジェクト構造なら

pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py

CDパッケージ

python -m tests.core_test # dont use .py

または外部のパッケージから/

python -m pkg.tests.core_test

.同じディレクトリのフォルダからインポートする場合は単一。ステップごとに1つ追加します。

hi/
  hello.py
how.py

how.py

from .hi import hello

hello.pyからインポートする場合は、

from .. import how

1
受け入れられた回答より優れている
GabrielBB

この例from .. import howでは、「how」ファイルから特定のクラス/メソッドをどのようにインポートしますか。同等のものを実行すると、from ..how import foo「トップレベルのパッケージを超えて相対的なインポートが試みられます」
James Hulse

3

古いスレッド。私は追加することが判明__all__= ['submodule', ...]__init__.pyファイル、その後使用してfrom <CURRENT_MODULE> import *、対象作品の罰金に。


3

を使用できますfrom pkg.components.core import GameLoopEvents。たとえば、私はpycharmを使用しています。以下は私のプロジェクト構造イメージです。ルートパッケージからインポートするだけで機能します。

ここに画像の説明を入力してください


3
これは私にはうまくいきませんでした。構成でパスを設定する必要がありましたか?
Mohammad Mahjoub

3

以下のようパオロは言った、我々は2つの呼び出し方法があります:

1) python -m tests.core_test
2) python tests/core_test.py

それらの間の1つの違いはsys.path [0]文字列です。インポート行うときにインタープリターがsys.pathを検索するので、次のようにできますtests/core_test.py

if __name__ == '__main__':
    import sys
    from pathlib import Path
    sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
    from components import core
    <other stuff>

さらにこの後、他のメソッドでcore_test.pyを実行できます。

cd tests
python core_test.py
python -m core_test
...

py36のみテスト済みです。


3

このアプローチは私にとってはうまくいき、いくつかの解決策よりも散らかっていません:

try:
  from ..components.core import GameLoopEvents
except ValueError:
  from components.core import GameLoopEvents

親ディレクトリは私のPYTHONPATHにあり__init__.py、親ディレクトリとこのディレクトリにファイルがあります。

上記は常にpython 2で機能しましたが、python 3は時々ImportErrorまたはModuleNotFoundError(後者はpython 3.6で新しく、ImportErrorのサブクラスです)にヒットするため、次の調整はpython 2と3の両方で機能します。

try:
  from ..components.core import GameLoopEvents
except ( ValueError, ImportError):
  from components.core import GameLoopEvents


1

誰かが回避策を探しているなら、私はそれを偶然見つけました。ここに少しコンテキストがあります。ファイルにあるメソッドの1つをテストしたいと思いました。内から実行すると

if __name__ == "__main__":

それは常に相対的な輸入について不平を言った。上記のソリューションを適用しようとしましたが、ネストされたファイルが多数あり、それぞれに複数のインポートがあるため、機能しませんでした。

これが私がしたことです。必要なメソッドをインポートして呼び出すランチャーという外部プログラムを作成しました。優れたソリューションではありませんが、機能します。


0

ここにみんなを怒らせるがかなりうまくいく1つの方法があります。テスト実行では:

ln -s ../components components

次に、通常どおりにコンポーネントをインポートします。


0

これは非常に混乱します。pycharmのようなIDEを使用している場合は、少し混乱します。私にとって何がうまくいったか:1. pycharmプロジェクト設定を行います(VEまたはpythonディレクトリからpythonを実行している場合)2.定義した方法に間違いはありません。時々それはfolder1.file1インポートクラスで動作します

それが機能しない場合は、import folder1.file1を使用します。3.環境変数がシステムで正しく記述されているか、コマンドライン引数で指定する必要があります。


-2

コードにはが含まれているためif __name__ == "__main__"、パッケージとしてインポートされないsys.path.append()ため、問題を解決するために使用する方が適切です。


私はif __name__ == "__main__"あなたのファイルを持っていることはインポートに関連する何かに違いをもたらすとは思わない。
user48956
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.