私はここにいました:
コピーしなかったURLもたくさんあります。SOにあるものもあれば、他のサイトにあるものもあります。すぐに解決策があると思ったときです。
永遠に繰り返される質問はこれです: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つにこの応答がありました:
相対インポートでは、モジュールのname属性を使用して、パッケージ階層内でのそのモジュールの位置を決定します。モジュールの名前にパッケージ情報が含まれていない場合(たとえば、「main」に設定されている場合)、相対インポートは、モジュールがファイルシステムの実際の場所にあるかどうかに関係なく、モジュールが最上位モジュールであるかのように解決されます。
上記の応答は有望に見えますが、それはすべて私には象形文字です。だから私の質問は、Pythonが「非パッケージで相対インポートを試みた」というメッセージを返さないようにするにはどうすればよいですか?おそらく-mを含む答えがあります。
Pythonがそのエラーメッセージを表示する理由、「非パッケージ」の意味、「パッケージ」を定義する理由と方法、幼稚園児が理解しやすい言葉で正確な答えを教えてください。
スクリプトとモジュール
これが説明です。短いバージョンでは、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
(注:インポートされた、直接実行されない)、その名前はpackage.subpackage1.moduleX
。インポートした場合moduleA
、その名前はになりますpackage.moduleA
。ただし、コマンドラインから直接実行する 場合moduleX
、その名前は代わりにになり__main__
、moduleA
コマンドラインから直接実行する場合、その名前はになります__main__
。モジュールがトップレベルのスクリプトとして実行されると、通常の名前は失われ、代わりにその名前がになり__main__
ます。
含まれているパッケージを介さずにモジュールにアクセスする
追加のしわがあります。モジュールの名前は、モジュールが含まれているディレクトリから「直接」インポートされたか、パッケージを介してインポートされたかによって異なります。これは、ディレクトリでPythonを実行し、同じディレクトリ(またはそのサブディレクトリ)にファイルをインポートしようとした場合にのみ違いがあります。たとえば、あなたがディレクトリにPythonインタプリタを起動した場合package/subpackage1
、次に行うimport moduleX
の名前はmoduleX
ちょうどになりmoduleX
、そしてませんpackage.subpackage1.moduleX
。これは、Pythonが起動時に現在のディレクトリを検索パスに追加するためです。現在のディレクトリでインポートするモジュールが見つかった場合、そのディレクトリがパッケージの一部であることがわかりません。また、パッケージ情報はモジュール名の一部にはなりません。
特別な場合は、インタプリタをインタラクティブに実行する場合です(たとえば、python
Pythonコードをその場で入力して入力を開始するだけです)。この場合、その対話型セッションの名前は__main__
です。
ここで、エラーメッセージにとって重要なことがあります。モジュールの名前にドットがない場合、それはパッケージの一部とは見なされません。ファイルが実際にディスク上のどこにあるかは関係ありません。重要なのはその名前が何であるかであり、その名前はロードした方法によって異なります。
次に、質問に含めた見積もりを見てください。
相対インポートでは、モジュールのname属性を使用して、パッケージ階層内でのそのモジュールの位置を決定します。モジュールの名前にパッケージ情報が含まれていない場合(たとえば、「main」に設定されている場合)、相対インポートは、モジュールがファイルシステムの実際の場所にあるかどうかに関係なく、モジュールが最上位モジュールであるかのように解決されます。
相対輸入...
相対インポートでは、モジュールの名前を使用して、モジュールがパッケージ内のどこにあるかを判別します。のような相対インポートを使用する場合from .. import foo
、ドットはパッケージ階層内のいくつかのレベルをステップアップすることを示します。たとえば、現在のモジュールの名前がpackage.subpackage1.moduleX
、の場合、..moduleA
はを意味しpackage.moduleA
ます。が機能するためにはfrom .. import
、モジュールの名前に少なくともimport
ステートメントにあるのと同じ数のドットが含まれている必要があります。
...パッケージ内の相対的なもののみ
ただし、モジュールの名前が__main__
、の場合、パッケージに含まれているとは見なされません。その名前にはドットがないため、そのfrom .. import
中でステートメントを使用することはできません。これを行おうとすると、「パッケージ以外の相対インポート」エラーが発生します。
スクリプトは相対をインポートできません
おそらくあなたがしたことはmoduleX
、コマンドラインから実行しようとしたことなどです。これを行うと、その名前はに設定されました。これは、その名前が__main__
パッケージ内にあることを明らかにしないため、その名前内の相対インポートが失敗することを意味します。これは、モジュールと同じディレクトリからPythonを実行し、そのモジュールをインポートしようとした場合にも発生することに注意してください。上記のように、Pythonは現在のディレクトリでモジュールを「早すぎる」と認識せずに検出するためです。パッケージの一部。
また、対話型インタプリタを実行する場合、その対話型セッションの「名前」は常にであることに注意してください__main__
。したがって、インタラクティブセッションから直接相対インポートを行うことはできません。相対インポートは、モジュールファイル内でのみ使用されます。
2つの解決策:
本当にmoduleX
直接実行したいが、それでもパッケージの一部と見なしたい場合は、を実行できますpython -m package.subpackage1.moduleX
。-m
ないトップレベルのスクリプトとして、モジュールとしてロードするためのPythonに指示します。
または、実際には実行し たくない場合は、内部の関数を使用するmoduleX
他のスクリプトを実行したいだけです。その場合は、入れてどこか-ではない内部のディレクトリ-と、それを実行します。内部でのようなことをすれば、それはうまくいくでしょう。myfile.py
moduleX
myfile.py
package
myfile.py
from package.moduleA import spam
ノート
これらのソリューションのいずれの場合も、パッケージディレクトリ(このpackage
例では)はPythonモジュールの検索パス(sys.path
)からアクセスできる必要があります。そうでない場合、パッケージ内の何も確実に使用することはできません。
Python 2.6以降、パッケージ解決を目的としたモジュールの「名前」は、その__name__
属性だけでなく、__package__
属性によっても決定されます。その__name__
ため、モジュールの「名前」を参照するために明示的な記号を使用することは避けています。Python 2.6以降、モジュールの「名前」は事実上__package__ + '.' + __name__
、またはそうである__name__
場合に限り__package__
ますNone
。)
これは本当にPython内の問題です。混乱の原因は、人々が誤って相対的なインポートをパスの相対的なものと見なしていないことです。
たとえば、faa.pyに書き込む場合:
from .. import foo
これは、実行中にPythonによって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
。(参照ラースと答えた私たちはケースのためか、我々はパッケージディレクトリの2つ以上のレベルを持っている場合、それを必要とし、その後、我々が使用するos.path.dirname(os.path.dirname(__file__))
ことが本当にありません-ブタ傷つけることもなく、これを行うには、安全なだけで十分ですどちらかここに。)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}; __name__ is {!r}'.format(__package__, __name__))
from .fileA import f1, f2
from .fileB import Class3
else:
print('Not a package; __name__ 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 __name__ == '__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
islib
および__name__
islib.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
。
以来(.
ある中でsys.path
、私たちは本当にここで、パスの絶対的なバージョンを追加する必要はありません。私たちがやりたい、より深いパッケージの入れ子構造が、ここはfrom ..otherlib.fileC import ...
、違いになります。あなたはこれをやっていない場合、あなたがすることができますすべてのsys.path
操作を完全に省略します。)
まだ癖があります。このすべてを外部から実行する場合:
$ python2 lib.foo
または:
$ python3 lib.foo
動作はの内容によって異なりlib/__init__.py
ます。それが存在し、空の場合、すべてが順調です。
Package named 'lib'; __name__ is '__main__'
しかし、lib/__init__.py
それ自体がインポートしroutine
て、routine.name
として直接エクスポートできる場合はlib.name
、次のようになります。
$ python2 lib.foo
Package named 'lib'; __name__ is 'lib.foo'
Package named 'lib'; __name__ is '__main__'
つまり、モジュールは2回インポートされます。1回はパッケージを介してインポートされ、もう1回はコードを__main__
実行するためにインポートされmain
ます。Python 3.6以降では、これについて警告しています。
$ python3 lib.routine
Package named 'lib'; __name__ 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'; __name__ is '__main__'
警告は新しいですが、警告し-について動作ではありません。これは、ダブルインポートトラップと呼ばれるものの一部です。(詳細については、問題27487を参照してください。)NickCoghlanは次のように述べています。
この次のトラップは、3.3を含む現在のすべてのバージョンのPythonに存在し、次の一般的なガイドラインにまとめることができます。「パッケージディレクトリまたはパッケージ内のディレクトリをPythonパスに直接追加しないでください」。
注ここではそのルールに違反している間、我々はそれを行うことのみロードされたファイルがされていないパッケージの一部としてロードされている、と私たちの修正は、具体的に私たちは、そのパッケージ内の他のファイルへのアクセスを許可するように設計されています。(そして、私が述べたように、おそらく単一レベルのパッケージに対してこれを行うべきではありません。)余分にクリーンにしたい場合は、これを次のように書き直すことができます。
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
、インポートを実行するのに十分な時間変更してから、元の状態に戻します(1つのコピー_i
を追加した場合にのみ1つのコピーを削除します_i
)。
それで、他の多くの人たちと一緒にこれについて考えた後、私はこの記事でドリアンBによって投稿された、Webサービスで使用するモジュールとクラスを開発する場所で私が抱えていた特定の問題を解決したメモに出くわしましたが、 PyCharmのデバッガー機能を使用して、コーディング中にそれらをテストできます。自己完結型のクラスでテストを実行するには、クラスファイルの最後に次のものを含めます。
if __name__ == '__main__':
# run test code here...
しかし、同じフォルダーに他のクラスやモジュールをインポートしたい場合は、すべてのインポートステートメントを相対表記からローカル参照に変更する必要があります(つまり、ドット(。)を削除します)。しかし、ドリアンの提案を読んだ後、彼の 'を試しました。ワンライナー」とそれはうまくいきました!これで、PyCharmでテストして、テスト対象の別のクラスでクラスを使用するとき、またはWebサービスで使用するときに、テストコードをそのままにしておくことができます。
# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __name__ == '__main__' or parent_module.__name__ == '__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としてテストされている別のモジュールで使用されているかどうかを確認します。おそらくこれは明らかですが、上記の相対的なインポートの問題に不満を持っている人がそれを利用できる場合に備えて、ここでこのメモを提供します。
これは私がお勧めしない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()
Pythonモジュールの検索パスを変更したくなく、スクリプトから比較的モジュールをロードする必要があるという同様の問題がありました(BrenBarnが上でうまく説明したように、「スクリプトはすべてに対して相対をインポートできない」にもかかわらず)。
そこで、次のハックを使用しました。残念ながら、imp
バージョン3.4以降で非推奨になったモジュールに依存して、を優先して削除されimportlib
ます。(これも可能importlib
ですか?わかりません。)それでも、ハックは今のところ機能します。
フォルダにあるスクリプトからmoduleX
inのメンバーにアクセスする例:subpackage1
subpackage2
#!/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 __name__ == '__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 __name__ == '__main__'
コードがグローバル名前空間から実行されているかどうかをテストするために使用される多くのPythonコードが表示されます。これにより、スクリプトを兼ねるモジュールを作成できます。
コンソールからこれらのインポートを実行しようとしましたか?
@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になり、相対的なインポートを見つけることができます。
相対インポートでは、モジュールのname属性を使用して、パッケージ階層内でのそのモジュールの位置を決定します。モジュールの名前にパッケージ情報が含まれていない場合(たとえば、「main」に設定されている場合)、相対インポートは、モジュールがファイルシステムの実際の場所にあるかどうかに関係なく、モジュールが最上位モジュールであるかのように解決されます。
この質問の視聴者を助けるかもしれない小さなpythonパッケージをPyPiに書きました。パッケージは、インポートファイルのディレクトリに直接移動することなく、パッケージ/プロジェクト内から上位レベルのパッケージを含むインポートを含むPythonファイルを実行できるようにする場合の回避策として機能します。https://pypi.org/project/import-anywhere/
別の汚いが回避策。あなたがあなたのパッケージのトップレベルにいると仮定します。
import sys
from os.path import dirname, basename
if __package__ is None:
sys.path.append('..')
__package__ = basename(dirname(sys.argv[0]))
from . import your_module
ここでの利点と別の答えは、IDEによって自動生成されるインポートを変更する必要がないことです。
Reba McEntire が息子の Shelby Blackstock と共有しているクリスマスの伝統について学びましょう。
メーガン・マークルとマライア・キャリーが自然な髪の上でどのように結合したかについて、メーガンの「アーキタイプ」ポッドキャストのエピソードで学びましょう.
ハリー王子が家族、特にチャールズ王とウィリアム王子との関係について望んでいると主張したある情報源を発見してください。
ワイノナ・ジャッドが、母親のナオミ・ジャッドが亡くなってから初めての感謝祭のお祝いを主催しているときに、彼女が今では家長であることをどのように認識したかを学びましょう.
Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?
The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!
ランボルギーニの創設者であるフェルッチオランボルギーニが100歳になるのは毎日ではありません(そうです、彼は死んでいて、まだ死んでいると思います。
Appleが自動車分野に参入するという噂はかなり前から渦巻いており、AppleウォッチャーがSixtyEight Researchという会社がAppleの自動車研究開発のシェル会社である可能性が高いと判断したとき、その渦巻きは本当に渦巻いた。また、会社が購入した車は1台だけであることが知られており、その車はAppleが何を考えているかについての手がかりでいっぱいになる可能性があることも伝えています。
太陽系の外側にある架空の大きな物体である惑星Xの探索は、何十年にもわたって人間を魅了してきました。その検索の最新の章は、地球の10倍の大きさで、公転周期が15であるほど遠くにある惑星を指しています。
カムニュートンは昨日、簡単な265ヤードと3回のタッチダウンでファルコンズを引き裂き、別の素晴らしいゲームをしました。その日のハイライトは、上のタッチダウンスローでした。これは、視聴するたびにばかげているだけです。
ロシアのフィギュアスケーター、カミラ・バリエバが関与したドーピング事件が整理されているため、チームは2022年北京冬季オリンピックで獲得したメダルを待っています。
何千人ものAmazonの買い物客がMulberry Silk Pillowcaseを推奨しており、現在販売中. シルクの枕カバーにはいくつかの色があり、髪を柔らかく肌を透明に保ちます。Amazonで最大46%オフになっている間にシルクの枕カバーを購入してください
ラファイエット警察署は、「不審な男性が女性に近づいた」という複数の苦情を受けて、12 月にパデュー大学の教授の捜査を開始しました。
私たちの周りの世界と同じように、言語は常に変化しています。以前の時代では、言語の変化は数年または数十年にわたって発生していましたが、現在では数日または数時間で変化する可能性があります。
認知症を患っている 91 歳のアジア人女性が最近、47 番街のアウター サンセット地区でロメオ ロレンゾ パーハムに襲われました。伝えられるところによると、被害者はサンフランシスコの通りを歩いていたところ、容疑者に近づき、攻撃を受け、暴行を受けました。
“And a river went out of Eden to water the garden, and from thence it was parted and became into four heads” Genesis 2:10. ? The heart is located in the middle of the thoracic cavity, pointing eastward.
人々にチャンスを与えることは、人生で少し遅すぎると私は信じています。寛大に。