为什么“import module”和“from package import module”会再次加载模块?
我在我的PYTHONPATH里有一个包,结构大概是这样的:
package/
__init__.py
module.py
print 'Loading module'
如果我在package/
这个目录下运行Python(或者在这个目录下写另一个模块),然后输入
import module
它会加载module.py,并且如预期那样打印出“Loading module”。但是,如果我接着输入
from package import module
它又会加载module.py,并且再次打印“Loading module”,这让我有点意外。我想知道这是为什么?
注意:我觉得我大概明白Python为什么这样做,因为对于import module
来说,sys.modules的键就是"module"
,但对于from package import module
来说,它的键是"package.module"
。所以我想知道的是,为什么这里的键会不同——为什么不使用文件的路径名作为键,这样Python就能按预期工作了呢?
2 个回答
这是当前模块系统的一个小缺陷。
当你导入 module 时,是从当前的命名空间导入,而这个命名空间没有名字。这个命名空间里的值和 package 里的值是一样的,但解释器却无法识别。
当你导入 package.module 时,你是从 package 的命名空间导入模块。
这就是为什么 main.py 应该放在包文件夹外面的原因。很多模块的组织结构是这样的:
package /
main.py
package /
sub_package1/
sub_package2/
sub_package3/
module1.py
module2.py
只调用 main.py 可以确保命名空间设置正确,也就是说 当前的命名空间是 main.py 的。这样就无法在 module2.py 中调用 import module1.py。你需要调用 import package.module1。这样做让事情变得更简单、更统一。
而且,是的,把当前文件夹当作没有名字的文件夹来导入确实是个坏主意。如果你有几个脚本,这会让人很头疼。但因为 Python 最开始就是这样设计的,所以也不是完全没有道理。
简单来说,当你从 package
目录运行代码时,你把 Python 配置搞错了。你不应该把这个目录放到 sys.path
中,因为它是在一个包里面的。
Python 不会把文件名当作关键字来用,因为它不是在导入一个文件,而是在导入一个模块。如果允许人们像这样写 'import c:\jim\my files\projects\code\stuff
',那就会引发很多麻烦。
再考虑一个例子:假设你在 ~/foo/package/
目录,而 ~/bar
在 PYTHONPATH
中,但 ~/bar
其实只是指向 ~/foo
的一个符号链接。你希望 Python 能帮你处理这个符号链接,去掉重复的部分吗?如果你把一个相对目录放到 PYTHONPATH
中,然后又换了目录呢?再比如,如果 'foo.py' 是指向 'bar.py' 的符号链接,你希望这两个也被去重吗?如果它们不是符号链接,而是完全相同的文件呢?在模糊的情况下加上复杂的规则,可能会让某些人觉得很方便,但对其他人来说却很麻烦。(Python 的哲学第12条:面对模糊,拒绝猜测的诱惑。)
Python 在这里做得很简单,你需要确保环境设置正确。现在,你可以说默认把当前目录放到 PYTHONPATH
中并不是个好主意,我可能也会同意你,但既然它在那儿,就应该遵循和其他路径条目一样的一致规则。如果你的应用程序是打算从任意目录运行的,可以通过在开始时执行 sys.path.remove('')
来移除当前目录。