python -m 到底是个啥?一行命令,看懂 Python 的隐藏入口
明明敲了 pip install requests,终端也显示 "Successfully installed",结果一跑代码,import requests 还是报错 ModuleNotFoundError。或者更离谱——你在公司电脑上给项目装了个包,转头发现它悄悄装进了系统 Python,把同事的环境搞得一团糟。这几乎是每个写 Python 的人都绕不开的第一课。问题往往不在手抖,而在那个被你忽略的前缀:python -m。今天把它讲透。它不只是 pip 前面一串多余的字母,而是 Python 很多隐藏能力的入口。别去找一个叫 xxx.py 的文件,而是去已安装的包里,找名为 xxx 的模块,把它当脚本一样运行。所以 python -m pip,等于"用当前这个 Python 解释器,去跑 pip 这个模块"。跟直接敲 pip 比,听着没差?大多数时候确实没差。可"大多数"不等于"永远"——坑,就藏在那些"不永远"里。下面这些,是我日常离不开的。复制就能用,建议先码住。- 装包就认准 python -m pip install机器上 Python 一多,裸敲 pip 到底指向谁,我心里都没底。但 python -m pip 会把包稳稳装进你正在用的这个 Python。👉 想装给谁,就用谁的 python 去跑 -m pip。
- 建虚拟环境:python -m venv myenv不用额外装 virtualenv,venv 标准库自带。跑完生成隔离目录,激活就是个干净小世界。
- 临时起个文件服务器:python -m http.server 8000在任意目录敲这一行,当前目录秒变网页服务器,浏览器开 localhost:8000 就能看文件。传个临时文件、调前端页面,比翻 U 盘快。
- 美化 JSON:python -m json.tool data.json挤成一行的 JSON,管道接一下立刻排整齐,顺手还校验了格式合不合法。命令行里的"格式化快捷键"。
- 给代码计时:python -m timeit "..."不用自己写 time 逻辑,一行搞定基准测试,Python 自动跑很多轮取稳定值。
- 再丢几个彩蛋python -m dis 脚本.py 把字节码反汇编出来看python -m pydoc requests 离线查文档python -m unittest 自动发现并跑测试
runpy 在背后干活写 python -m 模块名 时,真正跑的是标准库里的 runpy。它找到目标模块、加载进来、再当作 __main__ 执行一遍。所以模块里那句 if __name__ == "__main__": 照样生效。- 直接 python foo.py → Python 把 foo.py 所在目录 塞进 sys.path[0]。
- python -m pkg.foo
别小看这一行之差。目录结构一复杂,import 从哪儿找包,全看它。相对导入为什么非 -m 不可直接 python foo.py 时,Python 把文件当成顶层 __main__,不属于任何包。于是 from . import sibling 直接炸:"attempted relative import with no known parent package"。换成 python -m mypkg.foo,Python 知道 foo 是 mypkg 的一部分,相对导入立马就好使了。一句话:想用相对导入,就用 -m 把模块当包的一部分来跑。误区 1:pip 和 python -m pip 一样?单版本、干净环境里,基本一样。可只要机器上有多个 Python,裸 pip 就可能绑错解释器。养成 python -m pip 的习惯,能少背一半锅。误区 2:-m 只能跑 pip?反了。只要 Python 里装着的模块,理论上都能 -m 跑。上面那些 http.server、json.tool、timeit 就是证据。python -m 模块 = 用当前这个 Python,去跑指定的模块。说到底,它解决的不是能不能跑,而是——跑的是不是你以为那个。下次装包、起服务、查文档之前,先停一秒:这行命令,我是不是该加上 -m?「别再把 python -m 当成那串多余的字母——它救过无数人的环境。」