Python包导入混乱

Jos*_*del 5 python python-import python-2.7

如果我有一个像这样的目录结构:

\n\n
.\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 pkg\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 A.py\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 B.py\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 C.py\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n    \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 test\n        \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 script.py\n
Run Code Online (Sandbox Code Playgroud)\n\n

其中script.py包含:

\n\n
import pkg.B\nimport pkg.A\n\nprint pkg.A.test()\n
Run Code Online (Sandbox Code Playgroud)\n\n

A.py:

\n\n
import pkg.C\n\ndef test():\n    return pkg.B.test()\n
Run Code Online (Sandbox Code Playgroud)\n\n

B.py:

\n\n
def test():\n    return \'AAAA\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

C.py:

\n\n
def test3():\n    return \'C.test3\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

并且__init__.py是空的。

\n\n

如果A.py导入pkg.C,则代码有效。如果我注释掉该导入,则会失败并显示:

\n\n
Traceback (most recent call last):\n  File "pkg/test/script.py", line 9, in <module>\n    print pkg.A.test()\n  File "/Users/X/Desktop/importtest/pkg/A.py", line 4, in test\n    return pkg.B.test()\nNameError: global name \'pkg\' is not defined\n
Run Code Online (Sandbox Code Playgroud)\n\n

更改import pkg.C为 just import pkg,只要pkg.B导入到 中也可以工作script.py

\n\n

如果我注释掉pkg.Bfrom的导入,那么如果 I或inscript.py没有什么区别,我会收到错误:import pkgimport pkg.CA.py

\n\n
Traceback (most recent call last):\n  File "pkg/test/script.py", line 10, in <module>\n    print pkg.A.test()\n  File "/Users/X/Desktop/importtest/pkg/A.py", line 4, in test\n    return pkg.B.test()\nAttributeError: \'module\' object has no attribute \'B\' \n
Run Code Online (Sandbox Code Playgroud)\n\n

这是我期望的行为。

\n\n

所以基本问题是,为什么pkg.B.test()可以在A.pyif pkg.Bisn't imported 那里访问,ifpkg.B被导入script.pyA.py导入其他子模块?

\n\n

我仍然有点不清楚这里起作用的确切机制。非常感谢对描述导入逻辑的好文章的解释或指针。

\n

ash*_*bar 2

这个问题有点老了,但我在遇到类似问题时偶然发现了它。这就是我所理解的正在发生的事情:

模块导入一次

我们需要了解的第一件事是,模块仅加载一次!这意味着,例如,如果我们有:

X.py:

import Z
import Y
blah blah
Run Code Online (Sandbox Code Playgroud)

Y.py:

import Z
blah bloo
Run Code Online (Sandbox Code Playgroud)

运行X.py会给我们带来以下结果:

  1. 模块 Z 将在第一行加载。
  2. 模块 Y 将运行(作为 的结果import Y)。
  3. 虽然import Z是 module 中的第一行Y,但 moduleZ不会 再次加载。

注意:无论如何,无论是否加载模块,模块的名称都会导入到当前命名空间中,使其可访问,但它们都引用相同的模块对象!

导入子模块

当导入子模块时,例如pkg.A,您实际上是在加载包pkg __init__.py模块,此外,还加载 A.py 模块。而且您正在做的是将子模块作为属性添加到包的 module 中。IE:

>>> import pkg
>>> hasattr(pkg, 'A')
False
Run Code Online (Sandbox Code Playgroud)

重述 Python 会话后:

>>> import pkg.A
>>> hasattr(pkg, 'A')
True
Run Code Online (Sandbox Code Playgroud)

将各个部分组合在一起

因此,在执行script.py时,会加载模块并将属性添加到其模块对象中。当导入时,由于已经加载,因此未加载,而是将名称导入到的名称空间中,同时引用中导入的同一模块对象。因为属性添加到该对象 (in )会成功,即使它是在 中执行的。import pkg.BpkgBA.pypkg.CpkgpkgA.pyscript.pyBscript.pypkg.B.testA.py