Python绝对导入和相对导入

Python绝对导入和相对导入

oyxy2019 254 2023-11-17

背景

自定义一个工具包以供别人调用,出现了若干问题,现已解决并记录如下。

基本概念

1.模块(Module)
  • 模块是 Python 中最小单元的可执行代码单元。
  • 一个模块通常对应一个.py文件,其中包含了 Python 代码。
  • 模块可以包含变量、函数、类等代码元素,提供了一种组织和重用代码的方式。
2.包(Package)
  • 包是一种组织和管理模块的层次结构,用于解决命名冲突和组织代码。
  • 包是一个包含 __init__.py 文件的目录,该文件的存在告诉 Python 解释器,当前目录是一个包而不是一个普通目录,文件的内容可以为空,也可以写包的初始化代码,在导入包时会执行一遍。
  • 包内可以包含子模块和子包,形成一个层次结构。

示例:

my_package/
|-- __init__.py
|-- module1.py
|-- subpackage1/
|   |-- __init__.py
|   |-- module2.py
|-- subpackage2/
|   |-- __init__.py
|   |-- module3.py
3.关于 __init__.py 文件的作用
  • 标识包: __init__.py 文件的存在告诉 Python 解释器,当前目录是一个包而不是一个普通目录。
  • 包的初始化:__init__.py 中可以包含一些包的初始化代码,例如设置全局变量、加载模块等。
  • 定义包的接口: 可以在 __init__.py 中导入其他模块或子包,并将它们暴露给外部,形成包的公共接口。这样,当用户导入包时,可以直接访问包内部的模块和功能。
4.导入(import)

在Python中导包有以下几种方式:

  • import aaa (as a)
  • from aaa import bbb (as b)
  • from aaa import *(这种情况不能使用as重命名,代码中也不能使用aaa.调用,而是直接调用)

问题解决

1.如何区分是绝对导入还是相对导入

看开头,如果是以.开头就是相对导入,反之就是绝对导入。例如:

  • from package1.module1 import xxx是绝对导入
  • from .utils import *是相对导入
2.相对导入表示路径方式
  • .表示当前目录
  • ..表示当前目录的父目录,以此类推
  • 并且支持..model这种先返回再进入的方式
3.什么时候用绝对导入、相对导入

执行.py文件和__init__.py文件里使用绝对导入,模块代码里建议使用相对导入。

如果在执行.py文件中使用了相对导入,可能会报错:

    from .DAG_GCN import DAG_GCN
ImportError: attempted relative import with no known parent package
  • 这个错误表明 Python 解释器无法识别相对导入,因为当前模块不是在一个包内。相对导入要求模块必须位于一个包内,以便 Python 可以正确地解析相对导入的路径。所以,不要在执行文件中使用相对导入。
  • 如果在模块文件中使用了相对导入,在模块中添加if __name__ == '__main__':也是运行不了的,除非改为绝对导入。
4.在包内部导入模块出错

这是今天遇到的主要问题,从GitHub下载了一个名为’DCGL’的工具包,直接使用IDE打开,在项目内试图调用模块并运行时,遇到了报错:

    from DCGL.data import DataLoader, Simulate_Data
ModuleNotFoundError: No module named 'DCGL'

出现这种情况的原因是因为直接将’DCGL’当作了项目的根目录,导致找不到。

解决方法是,新建了一个名为’DCGL_ws’的目录,将’DCGL’目录包裹,然后再用IDE打开’DCGL_ws’,代码里就能识别到DCGL.了。

另外一种解决方法是,去掉import语句中前面的’DCGL.',这样也能找到子模块,但如果子模块中含有相对导入的代码的话,就会报错:

	from ...metrics import Metrics
ValueError: attempted relative import beyond top-level package
5.没有办法的办法

如果始终没法找到模块,有一种神奇的方法,在import之前添加下面代码就能成功导入:

import sys

# 填写实际模块的路径
sys.path.append('../')

# 打印所有python解释器可以搜索到的所有路径
print(sys.path)

参考资料

【python导包的几种方法 自定义包的生成以及导入详解】
https://blog.csdn.net/chinesepython/article/details/82113575

【Python 的模块化、相对导入、绝对导入、自定义包】
https://www.bilibili.com/video/BV1LB4y1U7dS

【第十七节4:绝对导入和相对导入】
https://www.bilibili.com/video/BV13M4y1172Y