Python 3での相対インポートについては、すでにかなりの質問があるようですが、それらの多くを調べた後も、私はまだ自分の問題に対する答えを見つけることができませんでした。だからここに問題があります。
以下に示すパッケージがあります
package/
__init__.py
A/
__init__.py
foo.py
test_A/
__init__.py
test.py
そして、私はtest.pyに一行あります。
from ..A import foo
今、私はpackage
のフォルダーにいます、そして私は走ります
python -m test_A.test
私はメッセージを得ました
"ValueError: attempted relative import beyond top-level package"
しかし、私がpackage
の親フォルダにいるなら、例えば、私は実行します:
cd ..
python -m package.test_A.test
すべて順調。
今私の質問は:私がpackage
のフォルダーにいて、私の理解に基づいてtest_Aサブパッケージの中のモジュールをtest_A.test
として実行したとき、..A
は1レベルだけ上がります、それはまだpackage
フォルダの中にあります、なぜそれはそれがbeyond top-level package
を言うメッセージを与えるか。このエラーメッセージの原因は何ですか?
編集:他の質問でこの質問へのより良い/より首尾一貫した答えがあります:
なぜ動かないのですか?パッケージがどこからロードされたのかpythonが記録していないためです。そのため、python -m test_A.test
を実行すると、test_A.test
が実際にpackage
に格納されているという知識は基本的に破棄されます(つまり、package
はパッケージとは見なされません)。 from ..A import foo
を試みると、それがもう持っていない情報(すなわち、ロードされた場所の兄弟ディレクトリ)にアクセスしようとしています。概念的には、math
のファイルでfrom ..os import path
を許可することに似ています。あなたはパッケージを区別したいのでこれは悪いでしょう。他のパッケージから何かを使用する必要がある場合は、from os import path
を使用してそれらをグローバルに参照し、それが$PATH
および$PYTHONPATH
を使用する場所でpythonを使用できるようにします。
python -m package.test_A.test
を使用するとき、from ..A import foo
を使用すると、package
の内容を追跡し、ロードされた場所の子ディレクトリにアクセスしているだけなので、問題なく解決できます。
なぜpythonは現在の作業ディレクトリをパッケージと見なさないのですか?いいえCLUE、しかし便利だと思います。
import sys
sys.path.append("..") # Adds higher directory to python modules path.
これを試して。私のために働きました。
仮定:package
ディレクトリにいる場合、A
とtest_A
は別々のパッケージです。
結論:..A
のインポートはパッケージ内でのみ許可されています。
その他の注意事項
相対インポートをパッケージ内でのみ利用可能にすることは、パッケージをsys.path
にある任意のパスに配置できるようにする場合に便利です。
編集:
これが異常であると思うのは私だけです。なぜ現在の作業ディレクトリがパッケージと見なされないのですか? - Multihunter
現在の作業ディレクトリは通常sys.pathにあります。そのため、そこにあるすべてのファイルはインポート可能です。これは、パッケージがまだ存在していなかったPython 2以降の動作です。実行中のディレクトリをパッケージにすると、 "import .A"および "import A"としてモジュールをインポートできるようになり、その場合は2つの異なるモジュールになります。多分これは考慮すべき矛盾です。
from package.A import foo
私はそれがよりはっきりしていると思う
import sys
sys.path.append("..")
これらの解決策のどれも3.6のようなフォルダ構造で私にはうまくいきませんでした:
package1/
subpackage1/
module1.py
package2/
subpackage2/
module2.py
私の目標はmodule1からmodule2にインポートすることでした。ついに私のために働いたのは、おかしなことに、十分でした:
import sys
sys.path.append(".")
これまでに説明した2ドットソリューションとは対照的に、シングルドットに注意してください。
編集:以下は私にとってこれを明確にするのに役立ちました:
import os
print (os.getcwd())
私の場合、作業ディレクトリは(予想外にも)プロジェクトのルートでした。
誰かがまだ提供されているすばらしい答えの後にまだ少し苦労しているならば、これをチェックすることを考えてください:
上記のサイトからの本質的な引用:
msgstr "同じようにプログラム的に同じことを指定できます:
インポートシステム
sys.path.append( '..')
もちろん、上のコードは他のimportステートメントの前に書かなければなりません。
事実の後でそれについて考えて、それがこのようになければならないことはかなり明白です。テストでsys.path.append( '..')を使用しようとしましたが、OPが投稿した問題に遭遇しました。私の他のインポートの前にimportとsys.path定義を追加することによって、私は問題を解決することができました。
最も人気のある答えが示唆するように、基本的にそれはあなたのPYTHONPATH
またはsys.path
が.
を含むがあなたのパッケージへのパスを含まないからです。また、相対インポートは、インポートが行われるファイルではなく、現在の作業ディレクトリに対する相対パスです。おかしな。
これを修正するには、まず相対インポートを絶対パスに変更してから、次のいずれかで開始します。
PYTHONPATH=/path/to/package python -m test_A.test
次の理由で、この方法で呼び出されたときにPythonパスを強制します。
python -m test_A.test
を使ってtest_A/test.py
と__== '__main__'
を使って__file__ == '/absolute/path/to/test_A/test.py'
を実行しています
つまり、test.py
では、あなたは絶対的なimport
をメインケースの状態で半保護して使用することができ、また一回限りのPythonパス操作を行うことができます。
from os import path
…
def main():
…
if __== '__main__':
import sys
sys.path.append(path.join(path.dirname(__file__), '..'))
from A import foo
exit(main())
上位フォルダーに__init__.py
がある場合は、そのinitファイルでインポートをimport file/path as alias
として初期化できます。それから、あなたはより低いスクリプトでそれを使うことができます:
import alias