从一行代码到结果只有四步:编辑、编译、执行、输出。编辑器只是把这四步包起来了。 源码 45 个字符,编译成 64 字节的字节码对象,常量表是 (0, 3, None)。 字节码前几条是 RESUME、LOAD_CONST、STORE_NAME——读常量、存变量,逐条对应源码。
教材这一节讲 PyCharm 的下载、安装、建工程、写第一个程序。 安装部分本沙箱不执行,但「运行」这件事背后的四步,可以现场拆开看。 看懂这四步,换任何编辑器都不慌——它们做的事完全一样。
一、下载与安装:只记两个选择
PyCharm 分专业版和社区版。社区版免费,做数据分析够用;专业版支持数据库和 Web 框架,要钱。 安装时有几个勾选项,值得勾的是「创建桌面快捷方式」和「关联 .py 文件」。 「关联 .py 文件」勾了之后,双击 .py 文件就用 PyCharm 打开,省事。
安装界面本沙箱不执行,这段照书上的步骤走就行。装完第一次启动会让你选主题,随便。
二、建工程:解释器选错一切白搭
PyCharm 的第一件事是建工程(Project),工程就是一个文件夹加一个解释器配置。 新建工程时要选 Interpreter:用哪个 Python 来跑这个工程里的代码。 选错解释器的后果很具体:代码里 import pandas 报「找不到模块」,而你在命令行里明明装好了。
原因是你装 pandas 用的那个 Python,和工程里选的那个不是同一个。 排错办法:在 PyCharm 的终端里敲 python -c "import sys; print(sys.executable)", 和外面命令行比一比。
三、运行背后的四步
点一下绿色的三角,代码就跑起来了。中间其实有四步:
| 步骤 | 做什么 | 谁来做 |
|---|---|---|
| 编辑 | 把代码写成 .py 文件 | 你 |
| 编译 | 翻成字节码,缓存在 pycache | Python 自动 |
| 执行 | 虚拟机逐条跑字节码 | Python |
| 输出 | 结果打到终端或写进文件 | 你的 print |
编辑器把四步串起来了,仅此而已。用记事本加命令行也能完成同样的事。 这一节要破除的迷信是:PyCharm 不是「Python 的必备组件」。它只是个更方便的写字板。
四、亲手看一遍字节码
用 compile 把一段源码翻成字节码对象,能看到两个数:字节码长度、常量表。 本篇那段五行代码,源码 45 个字符,字节码对象 64 字节,常量表是 (0, 3, None)。 常量表里为什么有 0 和 3?因为循环里用到了 range(3),还有个初始值 t = 0。
再看指令:RESUME 是开始执行,LOAD_CONST 读常量,STORE_NAME 存变量,LOAD_NAME 读全局名字, CALL 调函数。 看懂字节码不是必须的,但知道有这一层有用。它能解释一件事:为什么有的错在编译期就报, 有的要跑到那一行才报。
五、两种报错,两个时机
语法错(SyntaxError)在编译期就报,整份文件都跑不起来。 运行错(ZeroDivisionError 这类)编译能过,跑到那一行才炸,前面的代码已经执行过了。 这个区别很实用:语法错说明代码根本没开始跑; 运行错说明跑到一半了,前面可能有副作用已经发生(文件写了、数据删了)。
所以看到运行错,先想「它前面那句做了什么」,而不是只看报错那一行。 编辑器能帮的是四件事:语法高亮、自动补全、断点调试、一键运行。四件都是效率,不改变结果。
六、动手 Demo:从一行代码到结果:中间有四步
把一段五行代码编译成字节码,打出长度和常量表,再逐条执行。 最后分别制造一个语法错和一个运行错,看看它们在不同时点发作。
import dis
SRC = "t = 0\nfor i in range(3):\n t += i\nprint(t)\n"
code = compile(SRC, "<demo>", "exec")
print("源码 %d 个字符" % len(SRC))
print("字节码 %d 字节" % len(code.co_code))
print("常量表 %r" % (code.co_consts,))
for ins in list(dis.get_instructions(code))[:5]:
print(" %2d %-12s %s" % (ins.offset, ins.opname, ins.argval))
exec(code)
try:
compile("def f(:\n pass", "<bad>", "exec")
except SyntaxError as e:
print("语法错:%s" % e.msg)
try:
exec("1 / 0")
except ZeroDivisionError as e:
print("运行错:%s" % e)
实跑输出:
--------------------------------------------------------------------
从一行代码到结果:中间有四步
--------------------------------------------------------------------
① 四步是什么
--------------------------------------------------------------------
编辑:把代码写成 .py 文件。这一步跟 Python 无关,记事本也行。
编译:把源码翻成字节码,Python 自动做,结果缓存在 __pycache__ 里。
执行:虚拟机逐条跑字节码。
输出:结果打到终端,或者写进文件。
! PyCharm 这类编辑器只是把前两步和最后一步包起来了,中间没有魔法。
② 亲手走一遍:源码到字节码
--------------------------------------------------------------------
源码 45 个字符,编译成 64 字节的字节码对象。
字节码对象里还记着常量表 co_consts:(0, 3, None)。
执行它,结果是:
3
! 编译只做一次,执行可以很多次。这就是为什么循环里的代码不要在循环里 import。
③ 字节码长什么样
--------------------------------------------------------------------
| 偏移 | 指令 | 参数 |
|---:|---|---|
| 0 | RESUME | 0 |
| 2 | LOAD_CONST | 0 |
| 4 | STORE_NAME | t |
| 6 | LOAD_NAME | range |
| 8 | PUSH_NULL | None |
| 10 | LOAD_CONST | 3 |
| 12 | CALL | 1 |
| 20 | GET_ITER | None |
前几条是 LOAD_CONST、STORE_NAME,读常量、存变量,逐条对应源码。
! 看得懂字节码不是必须的,但知道「有这一层」能解释很多现象:为什么有的报错在编译期、
有的在运行期。
④ 两种报错,两个时机
--------------------------------------------------------------------
语法错的例子:编译期就报,invalid syntax。
运行错的例子:编译通过,跑到那一行才炸,division by zero。
! 语法错整份文件都跑不起来;运行错只炸那一条路径。两者修法完全不同。
⑤ 所以编辑器帮了什么
--------------------------------------------------------------------
帮你做四件事:语法高亮(早发现手误)、自动补全(少记名字)、断点调试(停在半路看变量)、
一键运行(省掉敲命令)。四件都是效率,没有一件改变结果。
! 换句话说:用记事本也能写完这本书的所有代码,只是慢。工具是加速器,不是入场券。
compile 只翻不跑,exec 只跑(拿到的已经是字节码)。分开用就能看清中间那层。
注意那个 exec(code) 打印出来的 3 没有缩进,因为它直接写到标准输出,不经过本课的行首排版。
这是真实输出。
30 秒实验
- 把 range(3) 改成 range(100),字节码长度会变吗?常量表呢?
- 把 print(t) 去掉,字节码会少哪些指令?
- 想一想:为什么「编译只做一次、执行可以很多次」这件事,能让程序变快?
常见误区
- 以为 PyCharm 是必装的:记事本加命令行一样能写,只是慢。
- 工程解释器和 pip 用的不是同一个:装完库 import 不到,多半是这个原因。
- 看到运行错只盯报错那一行:前面的代码可能已经改了数据、写了文件。
- 把 pycache 提交进版本库:那是编译产物,会自动重建。
自测三问(点开对答案)
1. 从 .py 文件到看到结果,中间有哪些步骤? —— 编辑源码、编译成字节码、虚拟机执行、输出结果。编辑器把这几步串在一起。 2. 语法错和运行错最大的区别是什么? —— 语法错在编译期就报,代码一行都没跑;运行错跑到那一行才炸,前面的代码已经执行过了。 3. 为什么常量表里会出现 0 和 3 这两个数? —— 因为源码里有 t = 0 和 range(3),这两个字面量都被存进常量表。PyCharm 适合写脚本。但数据分析更常见的工作方式是「一段代码一段话地试」,那需要另一种工具。 下一篇讲 Anaconda,再下一篇讲 Notebook。
- ☐ (本篇完,下一篇 2.5 Anaconda:一次装齐一整套)