Python包相对/绝对导入引发类对象一致性异常问题求助
Python包导入行为异常:相对/绝对导入导致类实例判断不一致
项目结构
src │ __init__.py │ main.py │ └───utils │ __init__.py │ data.py └───funcs.py
模块代码实现
utils/data.py
__all__ = [ 'Menu', 'Case' ] from enum import Enum class Menu(Enum): QUIT = 'exit' class Case: def __init__(self): self.a = 7
utils/funcs.py
__all__ = [ 'get_Menu_instance', 'get_Case_instance', ] from src.utils import data def get_Menu_instance(): return data.Menu.QUIT def get_Case_instance(): return data.Case()
main.py
import utils def main(): result = utils.get_Menu_instance() expected = utils.Menu.QUIT print(f'{isinstance(result, utils.Menu) = }\n' f'{isinstance(expected, utils.Menu) = }\n') result = utils.get_Case_instance() expected = utils.Case() print(f'{isinstance(result, utils.Case) = }\n' f'{isinstance(expected, utils.Case) = }\n') if __name__ == '__main__': main()
关键现象:导入方式影响isinstance结果
| 相对导入(utils/init.py) | 绝对导入(utils/init.py) |
|---|---|
| ```python |
from .data import *
from .funcs import *|python
from src.utils.data import *
from src.utils.funcs import *
| ```bash > py main.py isinstance(result, utils.Menu) = False isinstance(expected, utils.Menu) = True isinstance(result, utils.Case) = False isinstance(expected, utils.Case) = True ``` | ```bash > py main.py isinstance(result, utils.Menu) = True isinstance(expected, utils.Menu) = True isinstance(result, utils.Case) = True isinstance(expected, utils.Case) = True ``` | ## 原理解释 你推测的「存在两个不同的类对象」完全正确,核心原因是**Python中模块的唯一标识是其完整导入路径,不同路径会被视为独立模块,导致模块内的类被重复加载,生成互不关联的类对象**。 ### 相对导入场景的问题链: 1. 运行`main.py`时,Python会把当前目录(`src`的父目录)加入`sys.path`。 2. `main.py`执行`import utils`,Python从`sys.path`找到`utils`包,执行其`__init__.py`: - 通过相对导入`from .data import *`加载的模块标识是`utils.data`,并将`Menu`、`Case`绑定到`utils`命名空间。 3. 随后`utils/__init__.py`导入`funcs`,`funcs.py`执行`from src.utils import data`: - 这里的绝对导入会把模块标识解析为`src.utils.data`,Python认为这是一个新模块,会重新加载`data.py`,生成全新的`Menu`和`Case`类。 4. 最终结果: - `utils.Menu`来自`utils.data`模块; - `funcs`中返回的实例所属的`Menu`来自`src.utils.data`模块; - 两个`Menu`是完全独立的类,因此`isinstance`判断为`False`,`Case`同理。 ### 绝对导入场景的一致性: 当`utils/__init__.py`使用绝对导入`from src.utils.data import *`时: 1. `utils.Menu`和`funcs.py`中`from src.utils import data`得到的`data.Menu`,都来自同一个模块标识`src.utils.data`; 2. 模块只会被加载一次,类对象唯一,因此`isinstance`判断为`True`。 ### 验证方法 可以在`main.py`中添加代码打印类所属的模块路径,直观确认: ```python import utils from src.utils import data as src_data print(f"utils.Menu所在模块: {utils.Menu.__module__}") print(f"src_data.Menu所在模块: {src_data.Menu.__module__}")
相对导入场景下会输出:
utils.Menu所在模块: utils.data src_data.Menu所在模块: src.utils.data
绝对导入场景下则会输出相同的模块路径。
内容的提问来源于stack exchange,提问作者Gennadiy
相关产品推荐
相关产品推荐

