# 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相关吗?)
首先让我们来看一下字节码:
>>> 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。
现在什么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是返回的最后一个操作码的执行是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的情况下是等价的。
但是这是怎么回事?要理解这一点,我们应该看看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>",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’)。但等一下,一个模块呢?
在这里来了有趣的部分,如果我们有一些像从模块|包导入模块在这种情况下,第二次导入将发生,这是在import子句中的模块的导入。所以再次在OP示例中,我们现在需要导入pkg / a.py,首先我们在sys.modules中设置一个新模块的关键字pkg.a,然后继续我们的解释模块pkg / a.py,但在模块pkg / a.py完成导入它之前调用。进口b。
现在来第二)部分,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:这个答案是改写,因为当我试图再读一遍我说我的答案是坏配方,希望现在它会更有用:)