私はここにいました:
すぐに解決策が見つかると思ったときに、コピーしなかったたくさんのURL、一部のURL、他のサイトのURLなど。
永遠に繰り返される質問はこれです:Windows 7、32ビットPython 2.7.3では、どのように私はこの「パッケージ外の相対インポートの試み」というメッセージを解決するのですか?私はpep-0328にパッケージの正確なレプリカを作りました。
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
適切なモジュールにspamとeggsという名前の関数を作りました。当然、うまくいきませんでした。答えは明らかに私がリストした4番目のURLにありますが、それは私にとってすべての卒業生です。私が訪れたURLの1つにこの応答がありました。
相対インポートでは、モジュールの名前属性を使用して、パッケージ階層内でのそのモジュールの位置を決定します。モジュールの名前にパッケージ情報が含まれていない場合(たとえば「main」に設定されている場合)、モジュールが実際にファイルシステム上のどこにあるかに関係なく、相対インポートはモジュールが最上位モジュールであるかのように解決されます。
上記の応答は有望に見えますが、それはすべて私にとって象形文字です。それでは、私の質問ですが、どのようにしてPythonを「非パッケージでの相対インポートの試行」に戻さないのですか?おそらく-mを含む答えがあります。
なぜPythonがそのエラーメッセージを出すのかそれが非パッケージであることを意味する!、なぜそしてどうやって 'package'を定義するのか、そして正確な答えは、幼稚園児が理解するのに十分なほど簡単に言い表すことができる。
編集:インポートはコンソールから行われました。
スクリプトとモジュール
これが説明です。簡単に言うと、Pythonファイルを直接実行することと、そのファイルを別の場所からインポートすることとの間には大きな違いがあります。 ファイルがどのディレクトリにあるかを知っているだけでは、どのパッケージがPythonにあると考えているかはわかりません。さらに、ファイルをどのようにPythonにロードするかによって異なります。 (実行またはインポートによって)。
Pythonファイルをロードする方法は2つあります。トップレベルのスクリプトとして、またはモジュールとしてです。ファイルを直接実行する場合、たとえばコマンドラインでpython myfile.py
と入力して、ファイルを最上位スクリプトとしてロードします。 python -m myfile
を実行した場合、または他のファイル内でimport
ステートメントが見つかったときにロードされた場合は、モジュールとしてロードされます。一度に1つのトップレベルのスクリプトしか存在できません。トップレベルのスクリプトは、作業を開始するために実行したPythonファイルです。
命名
ファイルがロードされると、名前が付けられます(名前は__name__
属性に格納されています)。最上位スクリプトとしてロードされた場合、その名前は__main__
です。それがモジュールとしてロードされた場合、その名前はファイル名であり、その前にあるパッケージ/サブパッケージの名前がドットで区切られています。
だからあなたの例では例えば:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleA.py
moduleX
をインポートした場合(注:imported、直接実行されない)、その名前はpackage.subpackage1.moduleX
になります。 moduleA
をインポートした場合、その名前はpackage.moduleA
になります。ただし、を直接コマンドラインからmoduleX
を実行すると、その名前は代わりに__main__
になり、コマンドラインから直接moduleA
を実行するとその名前は__main__
になります。モジュールがトップレベルのスクリプトとして実行されると、そのモジュールは通常の名前を失い、その名前は代わりに__main__
になります。
そのパッケージを含まないモジュールへのアクセス
追加のしわがあります:モジュールの名前は、それがあるディレクトリから「直接」インポートされたのか、それともパッケージを介してインポートされたのかによって異なります。これは、Pythonをあるディレクトリで実行し、その同じディレクトリ(またはそのサブディレクトリ)にファイルをインポートしようとした場合にのみ効果があります。たとえば、Pythonのインタプリタをpackage/subpackage1
ディレクトリで起動してからimport moduleX
を実行すると、moduleX
の名前はpackage.subpackage1.moduleX
ではなくmoduleX
になります。これは、Pythonが起動時に現在のディレクトリを検索パスに追加するためです。インポート対象のモジュールが現在のディレクトリで見つかった場合、そのディレクトリがパッケージの一部であることを認識しないため、パッケージ情報はモジュールの名前の一部にはなりません。
特別な場合は、インタプリタを対話的に実行する場合です(たとえば、単にpython
と入力して、その場でPythonコードの入力を開始するなど)。この場合、その対話型セッションの名前は__main__
です。
これがあなたのエラーメッセージにとって重要なことです:モジュールの名前にドットがない場合、それはパッケージの一部であるとは見なされません。ファイルが実際にディスク上のどこにあるかは関係ありません。重要なのは、その名前が何であるかということだけです。そして、その名前は、ロード方法によって異なります。
質問に含まれている見積もりを見てみましょう。
相対インポートでは、モジュールの名前属性を使用して、パッケージ階層内でのそのモジュールの位置を決定します。モジュールの名前にパッケージ情報が含まれていない場合(たとえば「main」に設定されている場合)、モジュールが実際にファイルシステム上のどこにあるかに関係なく、相対インポートはモジュールが最上位モジュールであるかのように解決されます。
相対インポート...
相対インポートでは、モジュールのnameを使用して、パッケージのどこにあるのかを判断します。 from .. import foo
のような相対インポートを使用する場合、ドットはパッケージ階層内のいくつかのレベルをステップアップすることを示します。たとえば、現在のモジュールの名前がpackage.subpackage1.moduleX
の場合、..moduleA
はpackage.moduleA
を意味します。 from .. import
が機能するためには、モジュールの名前に少なくともimport
ステートメントのドット数と同じ数のドットが含まれている必要があります。
...パッケージ内でのみ相対的です
ただし、モジュールの名前が__main__
の場合、それはパッケージ内にあるとは見なされません。その名前にはドットがありません。したがって、その中にfrom .. import
ステートメントを使用することはできません。そうしようとすると、「パッケージ以外の相対インポート」エラーが発生します。
スクリプトは相対パスをインポートできません
たぶん、あなたはmoduleX
などをコマンドラインから実行しようとしました。これを行うと、その名前は__main__
に設定されました。これは、その名前からパッケージ内にあることが明らかにならないため、その中の相対インポートは失敗することを意味します。これは、モジュールと同じディレクトリからPythonを実行してそのモジュールをインポートしようとした場合にも発生することに注意してください。上で説明したように、Pythonはそれを認識せずに「早すぎる」モジュールを見つけるパッケージの一部。
また、対話型インタプリタを実行すると、その対話型セッションの「名前」は常に__main__
になります。したがって対話型セッションから直接相対インポートを行うことはできません。相対インポートはモジュールファイル内でのみ使用されます。
二つの解決策:
本当にmoduleX
を直接実行したいが、それでもパッケージの一部と見なしたい場合は、python -m package.subpackage1.moduleX
を実行できます。 -m
はPythonにそれをトップレベルのスクリプトとしてではなくモジュールとしてロードするように伝えます。
あるいは、実際にrunmoduleX
を実行したくない場合は、他のスクリプトを実行したいだけで、myfile.py
というようにはmoduleX
内で関数を使用します。その場合、myfile.py
をpackage
ディレクトリ内の別の場所に - notに配置して実行します。 myfile.py
の内側でfrom package.moduleA import spam
のようなことをしていれば、うまくいきます。
注意事項
どちらの方法でも、パッケージディレクトリ(この例ではpackage
)はPythonモジュールの検索パス(sys.path
)からアクセスできる必要があります。そうでなければ、あなたはパッケージの中で何かを確実に使うことは全くできないでしょう。
Python 2.6以降、パッケージ解決のためのモジュールの "名前"は、その__name__
属性だけでなく、__package__
属性によっても決まります。モジュールの "名前"を参照するために明示的なシンボル__name__
を使うのを避けているのはそのためです。 Python 2.6以降、モジュールの "名前"は事実上__package__ + '.' + __name__
、または__name__
がNone
の場合は単に__package__
になります。)
これは本当にPythonの問題です。 混乱の原点は、人々が誤って相対インポートを相対パスではないと見なしているということです。
例えば、 faa.py と書くと:
from .. import foo
実行中にパッケージの一部として faa.py が 識別およびロードされた場合 にのみ意味があります。その場合、 faa.py のモジュールの名前は、たとえば some_packagename.faa になります。現在のディレクトリにあるという理由だけでファイルがロードされた場合、pythonが実行されると、その名前はどのパッケージも参照せず、最終的に相対インポートは失敗します。
カレントディレクトリのモジュールを参照する簡単な解決策はこれを使うことです:
if __package__ is None or __package__ == '':
# uses current directory visibility
import foo
else:
# uses current package visibility
from . import foo
これは、例として適合するように修正された一般的なレシピで、パッケージとして記述されたPythonライブラリを処理するために現在使用しています。相互依存ファイルを含んでいます。 。これをlib.foo
と呼び、関数lib.fileA
およびf1
の場合はf2
、クラスlib.fileB
の場合はClass3
にアクセスする必要があるとしましょう。
これがどのように機能するかを説明するために、いくつかのprint
呼び出しを含めました。実際には、それらを削除することもできます(そしておそらくfrom __future__ import print_function
行も)。
この特定の例は、エントリをsys.path
に本当に挿入する必要がある場合に表示するには単純すぎます。 (複数のレベルのパッケージディレクトリがあり、os.path.dirname(os.path.dirname(__file__))
を使用する場合、doが必要な場合は Larsの回答 を参照してください。しかし、実際にはhurtでもありません。)if _i in sys.path
テストなしでこれを行うのも安全です。ただし、インポートされた各ファイルが同じパスを挿入する場合(たとえば、fileA
とfileB
の両方がパッケージからユーティリティをインポートする場合)、これはsys.path
を同じパスで何度も混乱させます。そのため、ボイラープレートにif _i not in sys.path
を含めると便利です。
from __future__ import print_function # only when showing how this works
if __package__:
print('Package named {!r}; __is {!r}'.format(__package__, __name__))
from .fileA import f1, f2
from .fileB import Class3
else:
print('Not a package; __is {!r}'.format(__name__))
# these next steps should be used only with care and if needed
# (remove the sys.path manipulation for simple cases!)
import os, sys
_i = os.path.dirname(os.path.abspath(__file__))
if _i not in sys.path:
print('inserting {!r} into sys.path'.format(_i))
sys.path.insert(0, _i)
else:
print('{!r} is already in sys.path'.format(_i))
del _i # clean up global name space
from fileA import f1, f2
from fileB import Class3
... all the code as usual ...
if __== '__main__':
import doctest, sys
ret = doctest.testmod()
sys.exit(0 if ret.failed == 0 else 1)
ここでの考え方はこれです(これらはすべてpython2.7とpython 3.xで同じように機能することに注意してください):
import lib
またはfrom lib import foo
を通常のコードからの通常のパッケージインポートとして実行する場合、__package
はlib
で、__name__
はlib.foo
です。最初のコードパスを使用して、.fileA
などからインポートします。
python lib/foo.py
として実行すると、__package__
はNoneになり、__name__
は__main__
になります。
2番目のコードパスを使用します。 lib
ディレクトリは既にsys.path
にあるため、追加する必要はありません。 fileA
などからインポートします。
lib
ディレクトリ内でpython foo.py
として実行する場合、動作はケース2と同じです。
lib
ディレクトリ内でpython -m foo
として実行する場合、動作はケース2および3に似ています。ただし、lib
ディレクトリへのパスはsys.path
にないため、インポートする前に追加してください。 Pythonを実行してからimport foo
を実行した場合も同様です。
(.
isはsys.path
であるため、ここにパスの絶対バージョンを実際に追加する必要はありません。これは、より深いパッケージのネストです。 from ..otherlib.fileC import ...
を実行したい構造が違いを生みます。これを行っていない場合は、sys.path
操作をすべて省略できます。
まだ癖があります。この全体を外部から実行する場合:
$ python2 lib.foo
または:
$ python3 lib.foo
動作はlib/__init__.py
の内容に依存します。それが存在し、が空の場合、すべて正常です:
Package named 'lib'; __is '__main__'
しかし、lib/__init__.py
itselfがroutine.name
をlib.name
として直接エクスポートできるようにroutine
をインポートすると、次のようになります。
$ python2 lib.foo
Package named 'lib'; __is 'lib.foo'
Package named 'lib'; __is '__main__'
つまり、モジュールは2回インポートされます。1回はパッケージ経由で、次に__main__
として再度インポートされ、main
コードが実行されます。 Python 3.6以降では、これについて警告します。
$ python3 lib.routine
Package named 'lib'; __is 'lib.foo'
[...]/runpy.py:125: RuntimeWarning: 'lib.foo' found in sys.modules
after import of package 'lib', but prior to execution of 'lib.foo';
this may result in unpredictable behaviour
warn(RuntimeWarning(msg))
Package named 'lib'; __is '__main__'
warningは新しいですが、警告された動作は新しいものではありません。これは、一部の呼び出し 二重インポートトラップ の一部です。 (詳細については、 issue 27487 を参照してください。)Nick Coghlanは次のように述べています。
この次のトラップは3.3を含むPythonの現在のすべてのバージョンに存在し、次の一般的なガイドラインに要約できます。「パッケージディレクトリまたはパッケージ内のディレクトリをPythonパスに直接追加しないでください。 「。
ここでそのルールに違反している間、ロードされるファイルがnotの一部としてロードされる場合にのみonlyこのパッケージ内の変更は、そのパッケージ内の他のファイルにアクセスできるように特別に設計されています。 (そして、私が述べたように、おそらく単一レベルのパッケージに対してこれを行うべきではありません。)さらにクリーンにしたい場合は、これを次のように書き換えることができます。
import os, sys
_i = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if _i not in sys.path:
sys.path.insert(0, _i)
else:
_i = None
from sub.fileA import f1, f2
from sub.fileB import Class3
if _i:
sys.path.remove(_i)
del _i
つまり、インポートを達成するのに十分な長さsys.path
を変更し、元の状態に戻します(_i
のコピーを1つ追加した場合に限り、_i
のコピーを1つ削除します) 。
他の多くの人と一緒にこのことについて考えた後、私はこの 記事 で Dorian B によって投稿された私が抱えていた特定の問題を解決したメモに出会いましたここではWebサービスで使用するためのモジュールとクラスを開発しますが、PyCharmのデバッガ機能を使用して、コーディング中にそれらをテストできるようにしたいと思います。自己完結型クラスでテストを実行するには、クラスファイルの最後に次のコードを追加します。
if __== '__main__':
# run test code here...
しかし、同じフォルダに他のクラスやモジュールをインポートしたい場合は、すべてのインポート文を相対表記法からローカル参照に変更する必要があります(つまり、ドット(。)を削除します)。ワンライナー 'そしてそれはうまくいった!このクラスを別のテスト対象クラスで使用するとき、または自分のWebサービスで使用するときは、PyCharmでテストしてテストコードをそのままにすることができます。
# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __== '__main__' or parent_module.__== '__main__':
from codex import Codex # these are in same folder as module under test!
from dblogger import DbLogger
else:
from .codex import Codex
from .dblogger import DbLogger
Ifステートメントは、このモジュールを main として実行しているかどうか、または main としてテストされている別のモジュールで使用されているかどうかを確認します。 。おそらくこれは明白ですが、上記の相対的なインポートの問題に不満を抱いている他の誰かがそれを利用できる場合に備えて、私はここでこのメモを提供します。
Pythonモジュール検索パスを変更したくなく、モジュールを比較的ロードする必要があるという同様の問題がありましたスクリプトから(" BrenBarnが上でうまく説明したように、スクリプトはすべてと一緒に相対をインポートできません」。
そこで、次のハックを使用しました。残念ながら、バージョン3.4以降廃止されたimp
モジュールに依存しており、importlib
が優先されます。 (これはimportlib
でも可能ですか?わかりません。)それでも、ハックは今のところ機能します。
subpackage1
フォルダーにあるスクリプトからsubpackage2
のmoduleX
のメンバーにアクセスする例:
#!/usr/bin/env python3
import inspect
import imp
import os
def get_script_dir(follow_symlinks=True):
"""
Return directory of code defining this very function.
Should work from a module as well as from a script.
"""
script_path = inspect.getabsfile(get_script_dir)
if follow_symlinks:
script_path = os.path.realpath(script_path)
return os.path.dirname(script_path)
# loading the module (hack, relying on deprecated imp-module)
PARENT_PATH = os.path.dirname(get_script_dir())
(x_file, x_path, x_desc) = imp.find_module('moduleX', [PARENT_PATH+'/'+'subpackage1'])
module_x = imp.load_module('subpackage1.moduleX', x_file, x_path, x_desc)
# importing a function and a value
function = module_x.my_function
VALUE = module_x.MY_CONST
よりクリーンなアプローチは、Federicoが述べているように、モジュールのロードに使用されるsys.pathを変更することです。
#!/usr/bin/env python3
if __== '__main__' and __package__ is None:
from os import sys, path
# __file__ should be defined in this case
PARENT_DIR = path.dirname(path.dirname(path.abspath(__file__)))
sys.path.append(PARENT_DIR)
from subpackage1.moduleX import *
問題のコードがグローバル名前空間で実行されるのか、インポートされたモジュールの一部として実行されるのかによって、__name__
は変わります。
コードがグローバル空間で実行されていない場合は、__name__
がモジュールの名前になります。グローバル名前空間で実行している場合 - たとえば、コンソールに入力した場合、またはpython.exe yourscriptnamehere.py
を使用してモジュールをスクリプトとして実行した場合は、__name__
は"__main__"
になります。
コードがグローバル名前空間から実行されているかどうかをテストするためにif __== '__main__'
を使った多くのpythonコードが見られます - スクリプトを兼ねるモジュールを持つことができます。
コンソールからこれらのインポートを実行しようとしましたか?
これは私がお勧めしない1つの解決策ですが、モジュールが単に生成されなかったいくつかの状況で役に立つかもしれません:
import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/your_dir")
import your_script
your_script.a_function()
相対インポートでは、モジュールの名前属性を使用して、パッケージ階層内でのそのモジュールの位置を決定します。モジュールの名前にパッケージ情報が含まれていない場合(たとえば「main」に設定されている場合)、モジュールが実際にファイルシステム上のどこにあるかに関係なく、相対インポートはモジュールが最上位モジュールであるかのように解決されます。
この質問の視聴者を助けるかもしれないPyPiに小さなpythonパッケージを書きました。インポートするファイルのディレクトリに直接入らずに、パッケージ/プロジェクト内から上位レベルのパッケージを含むインポートを含むpythonファイルを実行できるようにしたい場合、パッケージは回避策として機能します。 https://pypi.org/project/import-anywhere/
Pythonが私に戻ってこないようにするには「非パッケージで相対インポートを試みました」。パッケージ/
init.py subpackage1 /init.py moduleX.py moduleY.py subpackage2 /init.py moduleZ.py moduleA.py
このエラーは、親ファイルに相対インポートを適用している場合にのみ発生します。例えば、 "print(nameをコーディングした後、親ファイルは既にmainを返します。このファイルは既にmainであり、それ以降の親パッケージを返すことはできません。パッケージsubpackage1およびsubpackage2のファイルには相対インポートが必要です。親ディレクトリまたはモジュールを参照するために ".."を使用できます。ただし、親パッケージがすでに最上位のパッケージである場合は、その親ディレクトリ(パッケージ)より上位に移動できません。あなたが親に相対的なインポートを適用しているそのようなファイルは絶対インポートのアプリケーションで働くことができるだけです。プロジェクトのトップレベルを定義するPYTHON PATHの概念のために、あなたのファイルがサブパッケージに入っていても、あなたが親パッケージの絶対インポートを使用する場合、エラーは発生しません。
@BrenBarnの答えはそれをすべて言っていますが、あなたが私のようなら、理解するのに時間がかかるかもしれません。これが私の場合と、@ BrenBarnの答えがどのように適用されるかを示しています。おそらく役立つでしょう。
ケース
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleA.py
使い慣れた例を使用して、それにmoduleX.pyが..moduleAへの相対的なインポートを持つことを追加します。 moduleXをインポートしたsubpackage1ディレクトリにテストスクリプトを記述しようとしたが、OPに記述された恐ろしいエラーが発生したことを考えてみてください。
ソリューション
テストスクリプトをパッケージと同じレベルに移動し、package.subpackage1.moduleXをインポートします
説明
説明したように、相対的なインポートは現在の名前に対して相対的に行われます。テストスクリプトが同じディレクトリからmoduleXをインポートする場合、moduleX内のモジュール名はmoduleXです。相対的なインポートを検出すると、インタープリターはパッケージの階層をバックアップできません。これは既に最上位にあるためです。
上記からmoduleXをインポートすると、moduleX内の名前はpackage.subpackage1.moduleXになり、相対インポートが見つかります