很多初学者在写 Python 时,最先接触到的是“能跑起来”,但对代码到底是怎么被执行的、脚本与模块有什么区别、python xxx.py 和 python -m 为什么行为不同,并没有形成稳定认知。
这一节我们会先从解释器和运行方式讲起,把 Python 程序的最基础执行模型梳理清楚。只有把这些底层但高频的概念弄明白,后面的模块化、包结构和工程实践才不会越学越乱。
解释器到底是什么?
很多人在安装 Python 之后,会把“Python”理解成一个单一的软件。但更准确一点说,我们平时真正直接打交道的,其实是 Python 解释器。
解释器的作用,可以先简单理解为:读取你写下来的 Python 代码,并按语言规则把它执行起来。也就是说,当你在终端输入:
python app.py你并不是在“让系统直接运行一份叫做 Python 的源码”,而是在调用一个 Python 解释器程序,让它去读取 app.py 这个文件,再按照 Python 的语法和运行规则把里面的代码一步步执行出来。
之所以这一点值得专门拿出来讲,是因为它会直接影响你对后面很多现象的理解,比如:
- 为什么不同项目可以对应不同版本的 Python;
- 为什么编辑器里要选择解释器;
- 为什么虚拟环境本质上也在提供一份“项目自己的解释器入口”;
- 为什么同一份代码在不同 Python 版本下可能行为不同。
换句话说,解释器既是“代码如何开始执行”的入口,也是“环境差异为什么会影响运行结果”的关键节点。
从学习角度看,你现在不需要把解释器内部实现理解得太底层,也不用一上来就去研究编译器原理。你只需要先建立一个稳定认知:你写的是 Python 代码,而真正把这些代码变成程序行为的,是你当前所使用的那份解释器。
只要这一点清楚了,后面关于脚本运行、模块执行、虚拟环境和项目结构的问题,都会变得更容易理解。
运行一个 Python 脚本的几种方式
在刚开始学习 Python 时,最常见的运行方式通常就是直接执行一个文件。但实际上,Python 程序并不只有这一种启动方式。不同的启动方式,看起来只是命令略有区别,背后却会影响:
- 当前工作目录如何参与导入;
- 模块名如何确定;
- 相对导入能不能正常工作;
- 项目结构是否更适合长期维护。
也正因为如此,我们最好从一开始就把这些常见方式区分清楚。
直接执行文件
最直观的方式,就是直接把某个 .py 文件当作入口执行:
python app.py这种方式很好理解,也很适合刚开始练习时使用。你写一个文件,执行一个文件,心智负担最小。
它的优点主要有两个:
- 命令短,适合快速验证;
- 很贴近“脚本即入口”的使用方式。
但它也有明显边界。只要你的目录结构开始变复杂,比如引入多个模块、包结构、相对导入,这种方式就可能开始暴露问题。最常见的现象就是:同样一份代码,直接执行某个文件和从项目根目录按模块运行,导入结果不一样。
所以,直接执行文件很适合:
- 学语法;
- 写单文件脚本;
- 做快速实验。
但一旦代码开始走向项目化,这种方式就未必是最稳妥的主入口了。
通过模块方式运行
除了直接执行文件,你还可以把某个 Python 文件按“模块”的方式运行:
python -m package.module这里的重点不在于命令本身,而在于:你是在告诉解释器,“请把这个目标当作项目中的模块来运行”,而不是“把这个路径下的文件直接当入口执行”。
这种方式最大的价值,就是它更贴近项目化开发时的真实组织方式。尤其是在目录已经形成包结构之后,通过模块方式运行通常会更稳定,也更符合后面你在工程实践里会遇到的启动模式。
简单理解:
python app.py更像“直接跑一个文件”;python -m package.module更像“按项目结构运行一个模块”。
在小脚本阶段,这个区别看起来不大;但在多文件项目中,它会直接影响导入行为和执行上下文,因此非常值得尽早建立这个意识。
REPL、脚本与 Notebook 的差异
除了“运行一个 .py 文件”,Python 其实还有几种非常常见的使用形态,它们分别适合不同的任务:
1. REPL也就是你直接在终端输入 python 进入交互式环境,一行一行地执行代码。它特别适合快速试验某个表达式、看一个对象长什么样、验证某个小语法是否成立。
2. 脚本文件这是最常见、也最正式的使用方式。你把代码写进 .py 文件里,再通过解释器执行。脚本方式更适合保存逻辑、反复执行,也更适合后续演进成真正的项目代码。
3. Notebook比如 Jupyter Notebook 这样的环境。它允许你把代码、输出、图表和说明文本组织在同一个交互式文档里,非常适合数据分析、探索式实验和教学演示。
这三者之间并不是谁更高级,而是谁更适合当前问题:
- 想快速试一行代码:用 REPL;
- 想保存并长期维护逻辑:用脚本;
- 想边运行边分析结果:用 Notebook。
很多新手的问题,恰恰就在于把这些使用形态混在一起。比如把 Notebook 当长期工程入口,或者把 REPL 当主要学习方式。短期看很方便,长期就会越来越难组织知识和代码。
因此,一个更稳妥的原则是:REPL 用来试验,脚本用来沉淀,Notebook 用来探索。
__name__ 与程序入口判断
当你开始写多文件代码时,很快就会遇到这样一种写法:
if __name__ == "__main__":
main()很多人知道它“是入口判断”,但并不清楚它到底在判断什么。
你可以先这样理解:Python 在执行一个文件或模块时,会为它提供一个特殊变量 __name__。这个变量的值,取决于当前代码是“被直接运行”,还是“被别人导入”。
最常见的情况是:
- 如果当前文件就是启动入口,那么
__name__会等于"__main__"; - 如果当前文件是被别的模块导入进来的,那么
__name__通常会是它自己的模块名。
因此,这个判断语句的真正含义其实是:
- 如果当前文件是程序入口,就执行下面这段逻辑;
- 如果它只是被别的地方导入,就不要自动执行这段逻辑。
这有什么好处?好处非常实际:
- 你可以在一个文件里同时放函数定义和调试入口;
- 文件被导入时不会顺手把测试代码、示例代码也执行掉;
- 模块的“可复用逻辑”和“直接运行逻辑”可以清晰分开。
这也是为什么很多看起来“像脚本”的文件,在写得稍微规范一点后,都会把真正的入口逻辑放进 main(),再用 if __name__ == "__main__": 来显式托管启动时机。
它不是一个复杂语法点,但却是从“随手写文件”走向“有边界地组织代码”的很关键一步。
python xxx.py 和 python -m 有什么不同?
这是 Python 初学阶段最容易被忽略、但对后面项目结构影响非常大的一个问题。
表面上看,这两种写法都能把代码跑起来:
python app.py
python -m package.module可它们背后的视角并不一样。
当你执行 python app.py 时,你是在告诉解释器:“把这个文件路径对应的脚本直接跑起来。”而当你执行 python -m package.module 时,你是在告诉解释器:“请按模块解析规则,把这个模块当作入口运行。”
两者最重要的差异,通常体现在以下几个方面:
1. 模块身份不同直接执行文件时,它更像一个独立脚本;按模块运行时,它会带上更清晰的模块身份。
2. 导入路径感知不同在包结构项目中,模块方式通常更符合 Python 对项目结构的理解,因此导入行为也会更稳定。
3. 相对导入的兼容性不同很多“为什么相对导入报错”的问题,归根到底都是入口运行方式和模块组织方式没有对齐。
所以,一个很实用的经验是:
- 单文件脚本、快速实验:直接执行文件没问题;
- 包结构项目、服务项目、长期维护代码:优先考虑
python -m方式。
这并不是说以后你必须永远使用模块方式,而是你要知道:一旦代码已经不再只是一个零散文件,而是开始形成项目结构,python -m 往往才是更接近工程现实的启动方式。
很多 Python 项目里看起来“神秘的导入问题”,其实不是导入本身出了问题,而是程序一开始就用错了运行视角。
总结与预告
这一节我们先把 Python 程序最基础的运行方式讲清楚了,包括解释器、脚本执行、模块运行以及入口判断。很多后面看似复杂的工程问题,往往都和这些最基础的执行规则有关。
接下来,我们会把注意力放到最基础的数据类型上,先把数字、字符串、布尔值和 None 这些“高频但最容易被想当然”的成员重新认识一遍。