循环模块依赖和在Python中的相对导入
假设我们有两个具有循环依赖性的模块:
# a.py import b def f(): return b.y x = 42 # b.py import a def g(): return a.x y = 43 这两个模块在目录pkg中有一个空的__init__.py。导入pkg.a或pkg.b工作正常,如this answer中所述。如果我将导入更改为相对导入 from . import b 当尝试导入其中一个模块时,我收到一个ImportError: >>> import pkg.a Traceback (most recent call last): File "<stdin>",line 1,in <module> File "pkg/a.py",in <module> from . import b File "pkg/b.py",in <module> from . import a ImportError: cannot import name a 为什么会收到此错误?是不是与上面的情况大致相同? (这是与this question相关吗?) 编辑:这个问题不是关于软件设计。我知道避免循环依赖的方法,但我对错误的原因感兴趣。
首先让我们从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 hmm有趣:),所以从foo导入栏翻译成第一个IMPORT_NAME foo,它等效于import foo,然后IMPORT_FROM bar。 现在什么 让我们看看什么python做的时候,他发现 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是返回的最后一个操作码的执行是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()函数是安静方便,它尝试首先从模块v获取属性名称,如果它不存在它提高ImportError否则返回此属性。 现在这与相对导入有什么关系? 好的相对导入喜欢从。 import b例如在OP问题中来自pkg import b的情况下是等价的。 但是这是怎么回事?要理解这一点,我们应该看看 现在让我们把所有这些在一起,并试图找出为什么我们结束在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>",in <module> File "pkg/b.py",in <module> from . import a File "pkg/a.py",line 2,in <module> from . import a ImportError: cannot import name a # clear __builtin__._ 什么刚刚发生之前ImportError? 第一)从。 import a in pkg / b.py被调用,它被转换为如上所述从pkg import a,这又是在bytecode等价于import pkg; getattr(pkg,’a’)。但等一下,一个模块呢? 现在来第二)部分,pkg / b.py将被导入,它将首先尝试导入pkg,因为pkg已经导入,所以在我们的sys.modules中有一个键pkg,它将只返回值那个键。然后它将导入b在sys.modules中设置pkg.b键并开始解释。我们到达这条线路。导入! 但是请记住,pkg / a.py已经导入了,这意味着(sys.modules中的’pkg.a’)== True,所以导入将被跳过,只有getattr(pkg,’a’)被调用,会发生什么? python没有完成导入pkg / a.py!所以只有getattr(pkg,’a’)被调用,这将在import_from()函数中引发一个AttributeError,它将被转换为ImportError(不能导入名称a)。 免责声明:这是我自己的努力,以了解解释器内发生的事情,我很远是一个专家。 EDIt:这个答案是改写,因为当我试图再读一遍我说我的答案是坏配方,希望现在它会更有用:) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |