返回专题首页

Python 专题

进入 Python 的世界:理解解释器、脚本与模块运行

很多初学者在写 Python 时,最先接触到的是“能跑起来”,但对代码到底是怎么被执行的、脚本与模块有什么区别、`python xxx.py` 和 `python -m` 为什么行为不同,并没有形成稳定认知。

Python 专题第 03 篇 / 39 篇9 分钟

很多初学者在写 Python 时,最先接触到的是“能跑起来”,但对代码到底是怎么被执行的、脚本与模块有什么区别、python xxx.pypython -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.pypython -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 这些“高频但最容易被想当然”的成员重新认识一遍。