Python包导入混乱

5
如果我有一个目录结构如下:
.
└── pkg
    ├── A.py
    ├── B.py
    ├── C.py
    ├── __init__.py
    └── test
        └── script.py

script.py 中包含以下内容:

import pkg.B
import pkg.A

print pkg.A.test()

A.py:

import pkg.C

def test():
    return pkg.B.test()

B.py:

def test():
    return 'AAAA'

C.py:

def test3():
    return 'C.test3'

__init__.py文件是空的。

如果A.py导入pkg.C,那么代码可以正常工作。如果我注释掉这个导入语句,那么就会出现错误:

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

import pkg.C 改为 import pkg 也可以正常工作,只要在 script.py 中导入了 pkg.B
如果我在 script.py 中注释掉了对 pkg.B 的导入,则在 A.py 中无论是 import pkg 还是 import pkg.C 都会出错。
Traceback (most recent call last):
  File "pkg/test/script.py", line 10, in <module>
    print pkg.A.test()
  File "/Users/X/Desktop/importtest/pkg/A.py", line 4, in test
    return pkg.B.test()
AttributeError: 'module' object has no attribute 'B' 

我期待的行为是什么。

所以基本问题是,如果在A.py中没有导入pkg.B,那么为什么pkg.B.test()可以在A.py中访问,如果pkg.Bscript.py中被导入,并且A.py导入其他子模块?

我仍然有点不清楚这里的确切机制。对于解释或指向一个描述导入逻辑的好文章的指针,将不胜感激。


在Python 2.x中,您应该始终从顶级目录运行代码,因此使用python pkg/test/script.py并始终使用完整的导入import pkg....等。这应该解决任何导入问题。我也不是完全理解它。 - Simeon Visser
1个回答

2

这个问题有点老,但我在遇到类似问题时偶然看到了它。这是我所理解的情况:

模块只会被导入一次

我们需要首先了解的是,模块只会被加载一次! 这意味着,例如:

X.py:

import Z
import Y
blah blah

Y.py:

import Z
blah bloo

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

  1. 模块 Z 将在第一行加载。
  2. 模块 Y 将被运行(由于 import Y 的结果)。
  3. 尽管 import Z 是模块 Y 中的第一行,但模块 Z 不会再次加载。

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

导入子模块

当导入子模块,例如 pkg.A,实际上是在加载包 pkg__init__.py 模块,并且还要加载 A.py 模块。但是,您还要将子模块添加为包模块的属性。即:

>>> import pkg
>>> hasattr(pkg, 'A')
False

重新启动 Python 会话后:
>>> import pkg.A
>>> hasattr(pkg, 'A')
True

将这些部分组合在一起

因此,在执行import pkg.B时,pkg模块被加载,并将属性B添加到其模块对象中。当A.py导入pkg.C时,pkg不会被加载,因为它已经被加载过了。相反,名称pkg被导入到A.py的命名空间中,同时引用在script.py中导入的相同模块对象。由于该对象中添加了属性B(在script.py中),所以即使在A.py中执行pkg.B.test,也会成功。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接