Python中的导入

Python import 的搜索路径

  • 当前目录
  • 环境变量 PYTHONPATH 中指定的路径列表中依次搜索
  • 在 Python 安装路径的 lib 库中搜索

绝对导入

绝对导入有以下几种格式:

import foo
import foo.bar
import foo as bar
from foo import bar

除此之外还有一种 from foo import * 的形式,但是不推荐使用。原因是这样会打乱命名空间。模块导入的函数可能会与自定义的函数或变量重名,造成未知的 bug。

相对导入

PEP328 介绍了引入相对导入的原因,以及选择了哪种语法。

具体来说,使用句点来决定如何相对导入其他包或模块,多个文件层级使用多个句点。不过 PEP328 建议相对导入的层级不要超过两层

# 从当前目录的 foo 模块中导入 bar
from .foo import bar
# 从当前文件的上级目录的 foo 模块中导入 bar
from ..foo import bar

需要注意的是,存在相对导入的语句的模块,不能直接运行,否则会有异常

ValueError: Attempted relative import in non-package

原因如下:

在没有明确指定包结构的情况下,Python 是根据 __name__ 来决定一个模块在包中的结构的,如果是 __main__ 则它本身是顶层模块,没有包结构;如果是 A.B.C 结构,那么顶层模块是 A。基本上遵循这样的原则:

  • 如果是绝对导入,一个模块只能导入自身的子模块或和它的顶层模块同级别的模块及其子模块;
  • 如果是相对导入,一个模块必须有包结构且只能导入它的顶层模块内部的模块;

如果一个模块被直接运行,则它自己为顶层模块,不存在层次结构,所以找不到其他的相对路径。

如果想要在直接运行的模块中使用相对导入,那么可以通过将路径添加到 Python 检索路径的方式。

import sys
sys.path.append("/path/to/folder/containing/my_package")
import my_package

注意,相对导入与绝对导入仅用于包内部。如果是同一个目录下的两个文件,且目录不是一个包,那么每一个 python 文件都是一个独立的可以直接被其他模块导入的模块,就像导入标准库一样,它们不存在相对导入和绝对导入的问题。

导入注意事项

循环导入

如果创建了两个模块,二者互相导入对方,就会出现循环导入。运行任何一个模块,都会引发 AttributeError

在 《Flask Web 开发:基于 Python 的 Web 应用开发实战》中,第七章的大型结构有一个 hack 循环导入的方法——将导入代码放到模块的末尾

但是,这与“所有的导入语句都应该位于模块的顶部”的约定相悖,因此并不是一个好的方案。在解决循环导入的问题时,首先考虑的方案应该是重构代码。

参考链接

目录