16. 附录:交互模式的补充细节
对应官方文档:16. Appendix 一句话导读:把前面散落的交互模式小知识收成一页——报错怎么读、脚本怎么变成可执行命令、启动时自动跑点什么、以及全机器和单用户两种定制钩子。
附录不是新知识,更像说明书最后一页的小字:都是「平时用不上,一遇上就到处翻」的东西。官方这一章只有一个编号小节 16.1,下面挂四个子小节,村长严格按这个编号走。
16.1 交互模式(Interactive Mode)
官方现在会告诉你:交互 REPL 其实有两种。
- 经典基础解释器:所有平台都支持,只带最基本的行控制能力,也就是教程里那个朴素的
>>>。 - 新交互 shell:从 Python 3.13 起成为默认,支持彩色输出、多行编辑、历史浏览和粘贴模式。
新 shell 多了几个功能键,值得记一下:
| 按键 | 作用 |
|---|---|
F1 | 进入交互帮助浏览器(也就是 pydoc) |
F2 | 浏览命令行历史,去掉 >>> / ... 提示符,也不带输出,方便复制 |
F3 | 进入粘贴模式,粘一大段代码不容易被自动缩进搅乱;再按一次返回普通模式 |
退出方式也变了:新 shell 里直接敲 exit 或 quit 就行,不用加括号。想退回经典解释器,设个环境变量即可:
# 临时退回经典 REPL
PYTHON_BASIC_REPL=1 python3
16.1.1 错误处理(Error Handling)
出错时解释器会打印错误消息和栈回溯(traceback)。接下来发生什么,取决于代码是从哪儿来的:
- 交互模式:打印完回到主提示符
>>>,会话不断,你可以接着改。 - 从文件 / 脚本读入:打印完 traceback,以非零退出码结束进程,好让 shell 或 CI 知道「这步挂了」。
- 被
try的except子句接住的异常不算错误,不会触发上面这套流程。 - 少数错误无条件致命(解释器内部不一致、某些内存耗尽场景),同样非零退出。
- 所有错误信息写到标准错误流 stderr,正常输出写到标准输出 stdout;日志采集和重定向要靠这个区分。
# 被 except 接住的异常不会让脚本非零退出
try:
1 / 0
except ZeroDivisionError as exc:
print("接住了:", exc)
print("脚本继续执行到这里")
用命令行看退出码最直观:
python3 -c 'print("正常")' ; echo "退出码:$?"
python3 -c '1 / 0' ; echo "退出码:$?"
import sys
print("这行走 stdout")
print("这行走 stderr", file=sys.stderr)
在主提示符或次提示符按中断字符(通常是 Ctrl-C,有的键盘是 Delete),会丢弃当前这行输入并回到主提示符;但如果命令正在执行时按中断,则抛出 KeyboardInterrupt 异常,它可以被 try 接住。
# 模拟一次运行中的 Ctrl-C:证明它能被正常捕获
try:
raise KeyboardInterrupt
except KeyboardInterrupt:
print("Ctrl-C 被 try 接住,程序没有崩")
村长划重点:报错行为和「代码从哪来」有关,别把交互模式里「没事,还能继续敲」的直觉带到脚本里——脚本一报错就退出了。提问时贴文字版 traceback(连最后一行一起),别发截图;也要分清 red 的 stderr 和 black 的 stdout,
2>重定向的就是前者。
16.1.2 可执行 Python 脚本(Executable Python Scripts)
在 BSD 系的 Unix(含 macOS、Linux)上,Python 脚本可以像 shell 脚本一样直接执行,只要两件事:
- 文件头两个字符是
#!,第一行写成#!/usr/bin/env python3; - 给文件加上可执行位:
chmod +x。
# 在 Python 里本来就是注释符,所以这一行不会破坏语法;但位置必须是文件最开头,前面空一行都不行。另外这一行要以 Unix 的 \n 结尾,有些平台不吃 Windows 的 \r\n。
#!/usr/bin/env python3
"""hello.py:加上可执行位后可以直接 ./hello.py 运行。"""
import sys
print("你好,我是可执行脚本")
print("解释器:", sys.executable)
chmod +x hello.py
./hello.py
为什么用 /usr/bin/env python3 而不是写死 /usr/bin/python3?env 会顺着当前 PATH 找解释器——配合第 12 章的虚拟环境,激活哪个 venv 就用哪个 Python,写死路径就把 venv 绕过去了。
Windows 没有「可执行位」这个概念:安装器会自动把 .py 关联到 python.exe,双击即可运行;若改成 .pyw 后缀,还会把那个黑色控制台窗口隐藏掉,适合做图形界面小程序。
村长划重点:两个最容易翻车的细节——
#!必须是文件前两个字符(前面不能有 BOM、空行、注释),且首行行尾必须是\n。写完用python3 -m pip系列命令调试没问题、但./hello.py提示「bad interpreter」,八成就是从 Windows 拷过来带了\r\n。
16.1.3 交互式启动文件(The Interactive Startup File)
每次进 >>> 都要先 import 一堆东西?把环境变量 PYTHONSTARTUP 指向一个文件,启动时就会自动执行。它跟 Unix shell 的 .profile 是同一个思路。
几个边界要记清:
- 只在交互会话读取:跑脚本时不读;把
/dev/tty显式指定为命令源时也不读(虽然那种情况其他方面像交互会话)。 - 文件里的代码执行在交互会话同一个命名空间,因此里面
import的模块、def的函数,进>>>直接就能用。 - 可以在里面改提示符
sys.ps1/sys.ps2。 - 想在脚本里复用这份启动文件,必须自己显式
exec。
# ~/.pythonrc:由 PYTHONSTARTUP 指向,只在交互模式自动执行
import os
import sys
try:
import readline
except ImportError:
pass # Windows 等平台没有 readline,跳过
else:
import rlcompleter
readline.parse_and_bind("tab: complete")
sys.ps1 = "村长> "
sys.ps2 = "..... "
# 顺带读一份当前目录下的项目级启动文件
if os.path.isfile(".pythonrc.py"):
exec(open(".pythonrc.py", encoding="utf-8").read())
print("启动文件已加载,Tab 补全与自定义提示符就绪")
# 写进 ~/.zshrc 或 ~/.bashrc,之后每次新终端都生效
export PYTHONSTARTUP="$HOME/.pythonrc"
python3
脚本里想显式加载,就照官方给的写法:
import os
filename = os.environ.get("PYTHONSTARTUP")
if filename and os.path.isfile(filename):
with open(filename, encoding="utf-8") as fobj:
exec(fobj.read())
PYTHONSTARTUP 只在原生解释器生效,IPython 有自己的 ~/.ipython/profile_default/startup/ 目录(第 14 章提过),别把配置写错地方。
16.1.4 定制模块(The Customization Modules)
Python 还留了两个更底层的钩子:sitecustomize 和 usercustomize。它们属于 site 模块初始化流程,每次启动 Python 都会被导入,比 PYTHONSTARTUP 更早、影响面更广。
先找到用户级 site-packages 在哪:
import site
print("用户级 site-packages:", site.getusersitepackages())
print("全局 site-packages:", site.getsitepackages())
print("是否启用用户站点:", site.ENABLE_USER_SITE)
# 命令行里快速查同一个路径
python3 -c "import site; print(site.getusersitepackages())"
# 用 -s 临时禁用自动导入,排查「是不是定制模块搞的鬼」
python3 -s -c "import site; print(site.ENABLE_USER_SITE)"
把下面这份存成 usercustomize.py 丢进用户级目录,之后每次启动都会生效:
# usercustomize.py:放在用户级 site-packages 里,每次启动都会执行
import builtins
def clip(text, width=40):
return text if len(text) <= width else text[:width] + "..."
builtins.clip = clip
builtins.clip 因此在任何模块里都能直接调用,不用 import。两者的区别:
| 钩子 | 放在哪 | 谁来创建 | 导入顺序 | 怎么关掉 |
|---|---|---|---|---|
sitecustomize | 全局 site-packages | 机器管理员 | 先导入 | -S 或 -s |
usercustomize | 用户级 site-packages | 你自己 | 后导入 | -s |
加一条时间线帮你理清:-S 会整个跳过 site 初始化;site 跑完 → sitecustomize → usercustomize → (若进入交互模式)才轮到 PYTHONSTARTUP。
村长划重点:这两个钩子影响这台机器上的每一次 Python 启动,包括别人写的脚本、构建工具、CI。别往里塞业务逻辑或打印语句,最容易出现的灵异现象就是「我代码没改,怎么行为变了」。排查第一步永远是
python3 -s,能复现问题就说明锅在定制模块里。
本章小结
- 16.1 讲交互 REPL 的两种形态:经典解释器全平台可用;Python 3.13 起默认的新 shell 支持彩色、多行编辑、
F1帮助、F2历史浏览、F3粘贴模式,exit/quit不用加括号,可用PYTHON_BASIC_REPL=1退回。 - 16.1.1:出错打印消息与 traceback;交互模式回到主提示符,脚本模式非零退出;被
except捕获的不算错误;错误信息走 stderr、正常输出走 stdout;Ctrl-C在输入时丢弃当前行、在执行时抛KeyboardInterrupt。 - 16.1.2:
#!/usr/bin/env python3必须是文件头两个字符且以\n结尾,配chmod +x即可./hello.py;Windows 靠.py/.pyw关联,无可执行位。 - 16.1.3:
PYTHONSTARTUP只在交互会话生效,与会话共用命名空间,可改sys.ps1/sys.ps2;脚本里要用必须自己exec;IPython 不读它。 - 16.1.4:
sitecustomize(全局,管理员)与usercustomize(用户级,自己)由site在每次启动时导入,分别可用-S/-s禁用;排查异常行为先试python3 -s。