39

循環依存関係を持つ2つのモジュールがあるとします。

# a.py
import b
def f(): return b.y
x = 42

# b.py
import a
def g(): return a.x
y = 43

2つのモジュールはpkg、空のディレクトリにあります__init__.pyこの回答で説明されているように、インポートpkg.aまたは正常に動作します。インポートを相対インポートに変更した場合pkg.b

from . import b

ImportErrorモジュールの1つをインポートしようとすると、次のようになります。

>>> import pkg.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pkg/a.py", line 1, in <module>
    from . import b
  File "pkg/b.py", line 1, in <module>
    from . import a
ImportError: cannot import name a

なぜこのエラーが発生するのですか?状況は上記とほとんど同じではありませんか?(これはこの質問に関連していますか?)

編集:この質問はソフトウェア設計に関するものではありません。私は循環依存を回避する方法を知っていますが、とにかくエラーの理由に興味があります。

4

3 に答える 3

36

from importまず、Pythonでの動作から始めましょう。

まず、バイトコードを見てみましょう。

>>> def foo():
...     from foo import bar

>>> dis.dis(foo)
2           0 LOAD_CONST               1 (-1)
              3 LOAD_CONST               2 (('bar',))
              6 IMPORT_NAME              0 (foo)
              9 IMPORT_FROM              1 (bar)
             12 STORE_FAST               0 (bar)
             15 POP_TOP             
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE        

うーん面白い:)なのでfrom foo import bar、最初にに変換され、次にIMPORT_NAME fooに相当します。import fooIMPORT_FROM bar

さて、何IMPORT_FROMをしますか?

彼が見つけたときにPythonが何をするか見てみましょうIMPORT_FROM

TARGET(IMPORT_FROM)
     w = GETITEM(names, oparg);
     v = TOP();
     READ_TIMESTAMP(intr0);
     x = import_from(v, w);
     READ_TIMESTAMP(intr1);
     PUSH(x);
     if (x != NULL) DISPATCH();
     break;

基本的に、彼はインポート元の名前を取得します。これは、foo()関数内にありますbar。次に、フレームスタックからv、最後に実行されたオペコードの戻り値である値をポップし、次の2つの引数を使用しIMPORT_NAMEて関数を呼び出します。import_from()

static PyObject *
import_from(PyObject *v, PyObject *name)
{
    PyObject *x;

    x = PyObject_GetAttr(v, name);

    if (x == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
        PyErr_Format(PyExc_ImportError, "cannot import name %S", name);
    }
    return x;
}

import_from()関数は非常に簡単であることがわかるように、最初にnameモジュールから属性を取得しようとします。v存在しない場合は発生し、そうでない場合はImportErrorこの属性を返します。

さて、これは相対的なインポートと何の関係があるのでしょうか?

のような相対的なインポートfrom . import bは、たとえばOPの質問にある場合と同等ですfrom pkg import b

しかし、これはどのように起こりますか?これを理解するにはimport.c、Pythonのモジュール、特に関数get_parent()を確認する必要があります。.ご覧のとおり、関数はここにリストするのに長い間静かですが、一般に、相対的なインポートを見ると、モジュールに応じてドットを親パッケージに置き換えようとし__main__ます。これもOPの質問からのパッケージpkgです。

それでは、これらすべてをまとめて、OPの質問で動作が発生する理由を理解してみましょう。

このため、インポートを行うときにpythonが何をするかを確認できれば、Pythonがこの機能をすでに備えているので、追加の冗長モードで実行することで有効にできます-vv

したがって、コマンドラインを使用しますpython -vv -c 'import pkg.b'::

Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41) 
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.

import pkg # directory pkg
# trying pkg/__init__.so
# trying pkg/__init__module.so
# trying pkg/__init__.py
# pkg/__init__.pyc matches pkg/__init__.py
import pkg # precompiled from pkg/__init__.pyc
# trying pkg/b.so
# trying pkg/bmodule.so
# trying pkg/b.py
# pkg/b.pyc matches pkg/b.py
import pkg.b # precompiled from pkg/b.pyc
# trying pkg/a.so
# trying pkg/amodule.so
# trying pkg/a.py
# pkg/a.pyc matches pkg/a.py
import pkg.a # precompiled from pkg/a.pyc
#   clear[2] __name__
#   clear[2] __file__
#   clear[2] __package__
#   clear[2] __name__
#   clear[2] __file__
#   clear[2] __package__
...
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "pkg/b.py", line 1, in <module>
    from . import a
  File "pkg/a.py", line 2, in <module>
    from . import a
ImportError: cannot import name a
# clear __builtin__._

うーん、直前に何が起こったのImportError

最初に) from . import apkg/b.py呼び出されます。これは、上記で説明したようにに変換されます。from pkg import aこれもバイトコードで、と同等import pkg; getattr(pkg, 'a')です。しかし、ちょっと待ってくださいaモジュールでもありますか?!from module|package import moduleこの場合、import句のモジュールのインポートである2番目のインポートが発生するようなものがある場合は、ここで楽しい部分があります。したがって、OPの例でも、インポートする必要がありpkg/a.pyます。最初にsys.modules、新しいモジュールのキーを設定しpkg.a、次にモジュールの解釈を続行しますが、モジュールのインポートが完了pkg/a.pyする前に、を呼び出します。 。pkg/a.pyfrom . import b

次に、2番目の部分pkg/b.pyがインポートされます。次に、最初にインポートが試行されます。import pkgこれpkgは、すでにインポートされているため、キーが含まpkgれているためsys.modules、そのキーの値を返すだけです。次に、キーをimport b設定して解釈を開始します。そして、このラインに到着します!pkg.bsys.modulesfrom . import a

ただし、pkg/a.pyすでにインポートされていることを覚えておいてください。('pkg.a' in sys.modules) == Trueつまり、インポートはスキップされ、のみgetattr(pkg, 'a')が呼び出されますが、どうなるでしょうか。Pythonはインポートを完了しませんでしたpkg/a.py!?したがって、のみgetattr(pkg, 'a')が呼び出され、これAttributeErrorによりimport_from()関数内でが発生し、これはに変換されImportError(cannot import name a)ます。

免責事項:これは、通訳者の内部で何が起こっているのかを理解するための私自身の努力です。私は専門家になるにはほど遠いです。

EDIt:この答えは言い換えられました。もう一度読み込もうとしたときに、自分の答えがどのように定式化されていなかったかを指摘したからです。

于 2011-06-16T20:41:46.433 に答える
4

(ちなみに、相対的なインポートは重要ではありませんfrom pkg import。...を使用すると同じ例外が発生します。)

from foo import barここで起こっていることは、との違いはimport foo.bar、最初の値では、値barがpkgのモジュールであるfooか、モジュールの変数である可能性があることだと思いますfoobar2番目のケースでは、モジュール/パッケージ以外のものであることは無効です。

バーがモジュールであることがわかっている場合、の内容はそれを設定するのsys.modulesに十分であるため、これは重要です。モジュール内の変数である可能性がある場合foo、インタープリターは実際にの内容を調べる必要がありますfooが、インポート中fooは無効になります。実際のモジュールはまだ設定されていません。

from . import bar相対インポートの場合、現在のモジュールを含むパッケージからbarモジュールをインポートすることを意味すると理解していますが、これは実際には単なる構文糖衣であり、.名前は完全修飾名に変換されてに渡されるため__import__()、次のようになります。あいまいなようにすべての世界のためにfrom foo import bar

于 2011-06-15T03:38:11.567 に答える
1

追加の注として:

私は次のモジュール構造を持っていました:

base
 +guiStuff
   -gui
 +databaseStuff
   -db
 -basescript

でスクリプトを実行できるようにしたかったのですが、ファイルにがインポートされたimport base.basescriptため、エラーが発生して失敗しました。上記の外部スクリプトを使用して/を1回だけインポートしない限り、インポート全体の2回目の実行と上記のエラーが発生したため、登録されただけです。これを防ぐために、ベーススクリプトに次のように記述します。guiimport base.databaseStuff.dbbasebase__main__basebasebasescript

if  __name__ == '__main__' or \
  not '__main__' in sys.modules or \
  sys.modules['__main__'].__file__ != __file__: 
    #imports here
于 2012-05-31T18:37:50.190 に答える