You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 11:31:05