>>> from env_helper import info; info()
页面更新时间: 2023-12-27 09:24:46
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-16-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

3.2. 优先使用absolute import来导入模块

假设有如下文件结构,其中 app/sub1/string.py 中定义了一个 lower() 方法, 那么当在 mod1.py中: import string 之后再使用 string.lower() 方法时, 到底引用的是 sub1/string.py 中的 lower() 方法,还是Python标准库中 string 里面的 lower() 方法呢?

app/

__init__.py sub1/

__init__.py mod1.py string.py

sub2/

__init__.py mod2.py

从程序的输出会发现,它引用的是 app/sub1/string.py 中的 lower() 方法。 显然解释器 默认先从当前目录下搜索对应的模块,当搜到 string.py 的时候便停止搜索进行动态加载。 那么如果要使用 Python 自带的 string 模块中的方法,该怎么实现呢? 这就涉及 absolute import 和 relative import 相关的话题了。

在 Python 2.4 以前默认为隐式的 relative import , 局部范围的模块将覆盖重名的全局范围的模块。 如果要使用标注库中同名的模块,你不得不去深人考察 sys.modules —番。 显然这并不是一种非常友好的做法。

>>> import sys
>>> len(sys.modules)
980

Python 2.5 后虽然默认的仍然是 relative import , 但它为 absolute import 提供了一种新的机制,在模块中使用 from _future_ import absolute_import 语句进行说明后再进行导人。 同时它还通过点号提供了一种显式进行 relative import 的方法, . 表示当前目录, .. 表示当前目录的上一层目录。 例如想在 modl.py 中导人 string.py , 可以使用 from . import string 。 其中 modi 所在的包层次结构为 app.sub1.modi

但事情是不是就此结束了呢?远不止,使用显式 relative import 之后再运行程序一不小心你就有可能遇到这种错误

ValueError: Attempted relative import in non-package

这是什 么原因呢?这个问题产生的原因在于 relative import 使用模块的 __name__ 属性来决定当前 模块在包层次结构中的位置。 如果当前的模块名称中不包含任何包的信息,那么它将默认为 模块在包的顶层位置,而不管模块在文件系统中的实际位置。

而在 relative import 的情形下, __name__ 会随着文件加载方式的不同而发生改变, 上例中如在目录 app/subl/ 下运行 Python rnodl.py ,会发现模块的 _name__main_ ,但如果在目录 app/subl/ 下运行 Python -m modl.py ,会发现 _name_ 变为 modi 。 其中 -m 的作用是使得一个模块像脚本一样运行。 而无论以何种方式加载,当在包的内部运行脚本的时候,包相关的结构信息都会丟失,默认当 前脚本所在的位置为模块在包中的顶层位置,因此便会抛出异常。 如果确实需要将模块当作 脚本一样运行,解决方法之一是在包的顶层目录中加人参数 -m 运行该脚本, 上例中如果要 运行脚本modl_py可以在app所在的目录的位置输人 Python -m app.subI.modI 。 另一个解决 这个问题的方法是利用Python2.6在模块中引人的_package_ M性,设置_package_之 后,解释器会根据_package_和_name_的值来确定包的层次结构。 上面的例子中如果 将modl.py修改为以下形式便不会出现在包结构内运行模块对应的脚本时出错的情况了。

if __name__ == "__main__" and __package__ is None:

import sys import os.path sys.path[0] = os. path. abspath('./../../') print (sys.path[0]) import app.sub1 _package_ = str('app.sub1') from . import string

相比于absolute import, relative import在实际应用中反馈的问题较多,因此推荐优先使 用absolute import。

absolute import可读性和出现问题后的可跟踪性都更好。当项目的包层次 结构较为复杂的时候,显式relative import也是可以接受的,由于命名冲突的原因以及语义 模糊等原因,不推荐使用隐式的relative import,并且它在 Python3 中已经被移除。