三种启动方式实测:-m 换掉 sys.path[0],相对导入在直接运行时反而报错

举报
yd_232353574 发表于 2026/11/05 09:16:11 2026/11/05
【摘要】 当天实测三种启动方式下 sys.path[0]、__package__ 与平铺、相对导入的结果:直接运行时相对导入报 ImportError,-m 下平铺导入报 ModuleNotFoundError。另测 -P 开关与命名空间包边界。

同一个包,三种启动方式,三种结果

一个包,目录结构是这样:

mpkg/
  __init__.py
  modx.py
  show.py

show.py 里有一句 import modx。

我在本机(Windows 10 + NTFS,Python 3.13.14)把这几种跑法都试了一遍,每种都打印 os.getcwd()、sys.path[0]、__package__,再分别试平铺导入和相对导入。

跑法一,在包目录里直接跑 python show.py:

  • sys.path[0] 是包所在目录
  • __package__ 是 None
  • import modx 成功

跑法二,在上一级目录直接跑 python mpkg/show.py:

  • sys.path[0] 还是包所在目录,不是当前工作目录
  • __package__ 是 None
  • import modx 成功

跑法三,在上一级目录用模块方式跑 python -m mpkg.show:

  • sys.path[0] 变成了当前工作目录,也就是上一级
  • __package__ 是 'mpkg'
  • import modx 报 ModuleNotFoundError: No module named 'modx'

同一个文件,同一个包,三种跑法两种能用一种不能。差别就落在 sys.path[0] 这一处。

相对导入不是万能钥匙

常见说法是,包内引用改成相对导入 from . import modx,就跟在哪种方式启动无关了。我实测下来不成立:

  • 直接跑(cwd 在包内):from . import modx 报 ImportError: attempted relative import with no known parent package
  • 直接跑(cwd 在上一级):同样报这个错
  • 用 -m 跑(cwd 在上一级):from . import modx 成功

原因在 __package__。相对导入靠的是「当前模块属于哪个包」,而直接运行时 __package__ 是 None,Python 根本不知道当前文件属于哪个包,相对的那个点就没有锚点。

所以相对导入并非换个写法就万事大吉,它只在有包上下文的时候才成立。

一个反直觉的坑:模块方式跑过的模块,再 import 会执行两遍

我写了个 side.py,模块顶层就一句打印。跑 python -m mpkg.side,模块体执行一次,__name__ 是 __main__。

有意思的是跑完之后 sys.modules 里没有 mpkg.side 这一项,只有 mpkg。因为用 -m 启动时,模块是以 __main__ 的身份执行的,并没有按 mpkg.side 这个名字注册进 sys.modules。

这意味着随后再写一句 import mpkg.side,模块体会再执行一遍,这次 __name__ 是 mpkg.side。

两份模块对象互相独立,一份挂在 __main__ 下,一份挂在 mpkg.side 下。模块顶层做初始化、建连接、注册单例的时候,这两遍都要算进去。

模块方式要在包的上一级跑

在包目录内部执行 python -m mpkg.show,报的是另一种错:

ModuleNotFoundError: No module named 'mpkg'

跟上面那个「找不到 modx」完全不是一回事。这里是连包本身都没找到。原因是 -m 是拿当前工作目录当包根去找 mpkg 这个包的,你在包里面,压根看不见自己这个包。

3.11 起可以把这个注入整个关掉

Python 3.11 加了 -P 选项和 PYTHONSAFEPATH 环境变量,作用是不再把程序文件所在目录塞进搜索路径。

实测加上之后,直接运行的 sys.path[0] 变成了解释器自带的 python313.zip,程序文件目录不在里面了,于是连跑法一那种平铺 import modx 也一起失败,报 ModuleNotFoundError: No module named 'modx'。

想省掉「哪个目录被加进搜索路径」这类推理,可以直接开这个开关,代价是程序文件目录里的同级模块不再自动可见。

没有 __init__.py 的包也能用模块方式跑

我试了一个没有 __init__.py 的目录 nspkg,里面有 modx.py 和 show.py。

在上一级跑 python -m nspkg.show,__package__ 是 'nspkg',from . import modx 成功。这种没有 __init__.py 的目录叫命名空间包,模块方式照样能定位到它。

同一个文件直接跑,还是那句 attempted relative import with no known parent package。

排查顺序

遇到 ModuleNotFoundError 但文件明明在,先打这三行:

import os, sys
print(os.getcwd())
print(sys.path[:3])
print(__package__)

os.getcwd() 告诉你人在哪,sys.path 前三条告诉你会去哪找,__package__ 告诉你相对导入有没有锚点。再确认一下用的是哪种启动方式。

同一份代码在两种启动方式下,搜索路径和包上下文都不一样,这是「我这儿能跑你那儿不行」最常见的一个来源。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。