python优先加载同名包而非.py文件,因包路径匹配优先级高于模块文件;同名会导致导入歧义、属性访问失败及第三方库被劫持等隐患。

import 时 Python 优先加载包而非同名 .py 文件
当你在当前目录下同时存在 foo/(含 __init__.py)和 foo.py,执行 import foo 时,Python 会无条件选择 foo/ 包,完全忽略 foo.py。这不是 bug,而是导入机制的明确行为:包路径匹配优先级高于模块文件。
常见错误现象包括:
-
ImportError: cannot import name 'foo' from 'foo'—— 在foo/__init__.py里写from . import foo,试图导入自身模块,但此时foo指向的是包对象,不是foo.py模块 - 外部代码能成功
import foo,但内部无法通过foo.foo访问foo.py中的函数,因为foo是包,没有该属性
包内部放同名 .py 文件会导致模块解析歧义
如果包结构是 mypackage/__init__.py + mypackage/mypackage.py,那么 mypackage.py 是一个合法模块,但它和包共享同一命名空间前缀,容易触发隐式覆盖或循环引用。
典型问题场景:
-
from mypackage import mypackage:这行代码实际尝试从包中导入子模块mypackage.py,但如果__init__.py没有显式导出它,就会报ImportError -
import mypackage.mypackage看似合理,但一旦__init__.py里写了from .mypackage import *或类似逻辑,就可能因执行顺序导致未定义行为 - IDE 和类型检查器(如 mypy)常把这种结构识别为可疑命名,给出警告但不报错,埋下维护隐患
sys.path 顺序让本地同名文件“劫持”第三方库导入
冲突不仅发生在自定义包和模块之间,更常见于你新建了 json.py、requests.py 这类名字后,再 import json 就不再加载标准库或第三方库,而是加载你的空文件——因为 sys.path[0] 默认是当前目录。
关键点:
- 错误通常不表现为语法错误,而是运行时报
AttributeError(比如'module' object has no attribute 'dumps') -
print(json.__file__)能立刻暴露问题:路径指向你本地的json.py,而不是lib/python3.x/json/__init__.py - PyCharm 等工具可能缓存导入结果,改名后需重启解释器或清缓存,否则仍复现旧错误
真正安全的命名实践只有两个原则
所有冲突本质都源于命名空间污染,解决方式不是绕开机制,而是尊重 Python 的导入约定。
必须遵守的底线:
- 永远不要在项目根目录或任何
sys.path可达路径下,创建与标准库(os、sys、json)、常用第三方库(requests、numpy、flask)同名的.py文件 - 包内模块名不要与包名相同;例如
utils/包下就别放utils.py,改用utils/core.py或utils/helpers.py - 如果已发生冲突,
rm *.pyc __pycache__和import importlib; importlib.invalidate_caches()不能替代重命名——缓存只是表象,根源在sys.path查找逻辑
最易被忽略的一点:哪怕你没直接 import requests,只要某个依赖(比如 pip install some-pkg)内部调用了它,而你本地有 requests.py,整个依赖链就可能静默失败。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











