python 绝对引用和相对引用
python 引用基本原则
在 Python 里面,一个 .py 文件可以称为模块,包含了 __init__.py 文件夹的称为包(python3.3 之后不再要求包文件夹下必须包含 __init__.py)。
当一个模块被执行时,Python 会从 sys.path 给出的路径去找在模块中引入的包或其它模块,如果找不到,程序就会报错。
sys.path 添加机制
当使用 python 命令执行 python 程序时,python 解释器会自动将相关执行路径添加到 sys.path 中,从而在执行时,不光能够找到已安装或原始的包,还能够找到当前工程自定义的包。
python 的两种启动方式
- python */*/*.py:使用 python 直接执行 py 文件。
- python 解释器会将 py 文件所在的路径加入 sys.path 中,因此 py 文件上层的相关包和模块将无法被引用。
- 需要注意:这里只是将引入路径变到了 py 文件路径,但是当前文件路径并没有变化,即通过 os.getcwd() 获取到的仍然是执行 python */*/*.py 命令的路径。
- python -m *.*.*:注意最后一个 * 应该是一个 py 文件,但是不需要加 .py。
- python 解释器不再将 py 文件所在的路径加入 sys.path 中,而是直接将执行命令的当前文件路径加入 sys.path。因此,py 文件中的包层次都应该与当前文件路径统一,即如果执行 python -m a.b.c,那么如果 c.py 中需要引用 a/x.py,应该使用 import a.x。
- 从顶层包进行引用是推荐的方式,发布的包通常被安装在 Lib/site-packages 下,而 Lib/site-packages 会被默认添加到 sys.path 中。
特殊变量 __file__、__name__、__package__
- __file__:当前模块(py文件)的绝对文件路径。
- __name__: 当前模块的名称。如果是直接执行的,__name__ = __main__。如果为间接导入的,则包含对应的层级包名,比如 subpackage.subpackage1.moduleY。
- __package__: 标识当前模块所属的包。通常为 __name__ 中去掉 py 文件模块后的层级,比如 subpackage.subpackage1。
# 可以通过一下代码输出对应变量信息
print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(__file__,__name__,str(__package__)))
绝对引用
- 不管使用 python 还是 python -m 的方式执行 python 逻辑,绝对引用只需要根据刚刚介绍的添加到 sys.path 中的路径一层一层的索引对应包和模块即可。
相对引用
- 同理,不管使用 python 还是 python -m 的方式执行 python 逻辑,添加到 sys.path 中的路径不变。但是对应每个模块的 __package__ 会有所不同。
- 相对引用会先获取到 __package__ 中的值,然后基于给出的相对引用代码索引对应的模块或包。
- 相对引用引用语句以 . 表示当前 __package__ 层级,.. 表示 __package__ 上一层级,以此类推,... 表示 __package__ 上一层级的上一层级。
- 不过有两点需要注意。
- 使用了相对导入的模块文件不能作为顶层执行文件,即包含相对引用的逻辑的模块不能使用 python 直接执行。
- 相对导入只适用于顶级包内的模块,即 __package__ 的值如果为 subpackage.subpackage1,那么可以通过 .. 来引用 subpackage 下的其他包或模块。但如果 __package__ 的值为 subpackage1,那就没法使用 .. 进一步引用上层,否则会报错。此时,只能通过增加中间层级或者提升根层级的方式进行解决。