📊 第2章 搭建 Python 数据分析环境
📄 一句话概括

从一行代码到结果只有四步:编辑、编译、执行、输出。编辑器只是把这四步包起来了。 源码 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 秒实验

  1. 把 range(3) 改成 range(100),字节码长度会变吗?常量表呢?
  2. 把 print(t) 去掉,字节码会少哪些指令?
  3. 想一想:为什么「编译只做一次、执行可以很多次」这件事,能让程序变快?

常见误区

  1. 以为 PyCharm 是必装的:记事本加命令行一样能写,只是慢。
  2. 工程解释器和 pip 用的不是同一个:装完库 import 不到,多半是这个原因。
  3. 看到运行错只盯报错那一行:前面的代码可能已经改了数据、写了文件。
  4. 把 pycache 提交进版本库:那是编译产物,会自动重建。
自测三问(点开对答案) 1. 从 .py 文件到看到结果,中间有哪些步骤? —— 编辑源码、编译成字节码、虚拟机执行、输出结果。编辑器把这几步串在一起。 2. 语法错和运行错最大的区别是什么? —— 语法错在编译期就报,代码一行都没跑;运行错跑到那一行才炸,前面的代码已经执行过了。 3. 为什么常量表里会出现 0 和 3 这两个数? —— 因为源码里有 t = 0 和 range(3),这两个字面量都被存进常量表。

PyCharm 适合写脚本。但数据分析更常见的工作方式是「一段代码一段话地试」,那需要另一种工具。 下一篇讲 Anaconda,再下一篇讲 Notebook。

相关笔记