Skip to content

Python 3.13 改进的 REPL ​

Python 3.13 对交互式解释器(REPL)做了一次大改版:引入了基于 PyPy 项目 PyREPL 的全新实现,首次让官方 REPL 具备多行编辑、语法高亮、彩色回溯、可浏览的历史记录等能力。这些改动来自 PEP 762,是 3.13 最受关注的用户体验改进之一。

前置知识

阅读本节前,建议先了解:REPL 基础操作

基础概念 ​

在 3.13 之前,官方 REPL 基于标准库 readline(GNU Readline 或 libedit)实现,功能有限。新 REPL 改用 _pyrepl 模块——它移植自 PyPy 的 pyrepl,用纯 Python 重新实现了行编辑与终端控制。

新旧 REPL 的能力对比:

能力3.12 及以前3.13 新 REPL
多行编辑不支持,按回车即执行支持,可在多行间移动光标
语法高亮无支持,提示符与代码着色
彩色回溯需第三方库默认彩色
历史浏览依赖 readline 的 Ctrl-R内置 F2 历史浏览器
交互式帮助需 help(obj)F1 打开浏览界面
直接 exit/quit/help必须加括号可不加括号
粘贴多行代码易出错支持粘贴模式(F3)

需要理解的一点是:新 REPL 不是默认在所有环境生效。当标准输入不是终端(例如管道输入)时,或显式设置了禁用变量,解释器会回退到旧实现。

哪些情况仍会使用旧 REPL:

  • 通过管道输入代码,如 echo "print(1)" | python;
  • stdin 不是 TTY(python < script.py);
  • 设置了环境变量 PYTHON_BASIC_REPL;
  • 某些终端不支持所需的控制序列时。

语法与用法 ​

判断当前用的是哪套 REPL,最直接的方式是观察启动横幅(仅新 REPL 会打印带有版本与平台的欢迎信息),或检查模块是否存在:

python
# 仅 Python 3.13+ 存在该模块
import _pyrepl
print(_pyrepl.__name__)
# _pyrepl

在 3.12 上执行会抛 ModuleNotFoundError。也可以在 Shell 中检查:

bash
python -c "import importlib.util; print(importlib.util.find_spec('_pyrepl') is not None)"
# True(3.13+);False(3.12-)

新 REPL 中可直接调用这些名字,无需括号:

text
>>> help          # 进入交互式帮助,等价于 help()
>>> exit          # 退出解释器
>>> quit          # 同上

这在旧 REPL 中会报错:直接输入 exit 只是把 Quitter 对象打印出来,并不会退出。

新 REPL 的快捷键一览:

按键功能
F1打开交互式帮助浏览器(基于 pydoc)
F2打开历史浏览器,可跨会话浏览
F3切换粘贴模式,便于粘贴多行代码
Ctrl-L清屏
Ctrl-C中断当前输入或执行
Ctrl-D发送 EOF,空行时退出
Ctrl-R反向增量搜索历史(沿用 readline 习惯)
Ctrl-N / Ctrl-P下一条 / 上一条历史
方向键在代码中移动光标(支持多行)

按 F2 打开的历史浏览器会跳过输出与提示符,只显示你输入过的代码行,因此比向上翻屏更高效。

详细说明 ​

PEP 762 的内容与动机 ​

PEP 762 标题为 "REPL-acing the default REPL",状态为 Final,属于 Informational 类型,于 2024 年 10 月 11 日创建,目标版本为 Python 3.13。它的核心主张是:把 PyPy 长期使用的 pyrepl 方案引入 CPython,替换原先基于 readline 的最小实现。

改动前的实现困难在于:3.12 及以前的交互式外壳由 C 语言实现,是解析器的一个特殊模式,维护与扩展都很困难;它依赖 GNU readline(或等价库)提供光标移动与历史记录,未链接该库时功能极为有限。这导致官方 REPL 长期缺少现代终端应有的能力。改用 Python 实现后,贡献门槛显著降低,功能迭代也更快。

带来的具体改进包括:

  • 多行编辑:在定义的函数或循环中,可以上下移动光标修改已输入的行,而不是只能改最后一行。
  • 历史记录保留编辑过程:一次输入的多行代码作为一条历史条目保存,可整体重新调出。
  • 语法高亮:输入时实时着色,帮助识别关键字、字符串、注释。
  • 彩色回溯:异常回溯中的文件名、行号、代码行默认着色,更易定位。
  • 交互式帮助浏览器:F1 打开 pydoc 风格的浏览界面,并拥有独立的历史记录。
  • 直接调用 exit/quit/help:减少初学者的困惑。
  • 粘贴模式:避免多行代码粘进终端时被逐行提前执行。

需要澄清的是:PEP 762 是 Informational(信息性)文档,它描述设计要求与实现方案,本身不改变语言语义。REPL 的变化不影响脚本执行行为。

颜色控制 ​

新 REPL 与 Python 3.13 的彩色回溯都受环境变量控制:

环境变量作用
PYTHON_COLORS显式开启或关闭颜色(1/0)
NO_COLOR设置后关闭颜色(遵循 no-color.org 约定)
FORCE_COLOR强制开启颜色,即使输出不是终端

优先级方面,NO_COLOR 与 FORCE_COLOR 遵循社区约定;PYTHON_COLORS 是 Python 自己的开关。实际使用中:

bash
# 关闭所有着色(包括 REPL 与回溯)
NO_COLOR=1 python

# 明确关闭
PYTHON_COLORS=0 python

# 在重定向输出时仍强制着色(例如把回溯写进日志文件)
FORCE_COLOR=1 python script.py > traceback.log 2>&1

在代码中可以判断当前输出流是否为终端,从而决定是否要让日志带上颜色:

python
import sys

# 判断 stderr 是否连接到终端;不是终端时通常不必输出颜色
is_tty = sys.stderr.isatty()
print(f"stderr 是终端:{is_tty}")

# 读取解释器启动时的相关选项
print(f"交互模式:{sys.flags.interactive}")
print(f"忽略环境变量与 site:{sys.flags.ignore_environment}")

若要在程序内彻底关闭回溯颜色,最简便的方式是设置环境变量后启动,而不是在运行时改。

关闭新 REPL ​

如果新 REPL 在你的终端上有兼容性问题(例如某些远程终端、旧版 tmux、Windows 的部分终端组合),可以回退到旧实现:

bash
# 方式一:环境变量
PYTHON_BASIC_REPL=1 python

# 方式二:写入 Shell 配置,长期生效
echo 'export PYTHON_BASIC_REPL=1' >> ~/.zshrc

回退后,多行编辑、F1/F2/F3 等功能将不再可用,行为与 3.12 一致。

F1 帮助浏览器的用法 ​

按 F1 会打开一个基于 pydoc 的浏览界面。它的特点是:

  • 可以在不退出 REPL 的情况下翻阅文档;
  • 拥有独立的历史记录,与代码输入历史互不干扰;
  • 支持搜索与跳转(具体按键见界面底部提示);
  • 按 q 退出回到 REPL。

这与直接输入 help(obj) 的区别在于:F1 打开的是一个持续存在的浏览器,适合连续查阅多个对象;而 help(obj) 只在终端分页输出一次。

F2 历史浏览器的价值 ​

传统做法是反复按方向键上翻,但输出内容也会混在其中,定位困难。F2 历史浏览器只列出输入过的代码,并附带时间与顺序,便于:

  • 找回几十分钟前试验过的片段;
  • 跳过大量输出直接选中某条命令;
  • 跨会话查找(历史保存在文件中,见 多行编辑与历史记录)。

F3 粘贴模式 ​

把多行代码直接粘贴进终端时,旧 REPL 会在遇到换行时立即执行,导致代码被拆散。F3 粘贴模式会临时把回车当作「插入换行」而非「提交」,直到你再次按 F3 或按特定组合键确认。这对从编辑器或文档复制示例代码尤其有用。

与 IPython 的定位差异 ​

维度3.13 新 REPLIPython
是否内置是需安装
富文本/图片输出无支持
魔术命令无有(%timeit、%%bash 等)
自动补全基础强大且可扩展
启动速度快相对慢
适合场景日常探查、学习数据科学、复杂调试

新 REPL 的目标是「让默认体验足够好」,而不是取代 IPython。需要更丰富的交互能力时,仍可安装 IPython(见 颜色高亮与自动补全)。

自定义命令 ​

新 REPL 支持自定义命令(custom commands)。这类命令以特定前缀触发,用于在交互会话中执行解释器层面的操作。它由一个内置的命令注册表驱动,扩展了传统 REPL 只能「输入 Python 代码」的限制。

python
# 查看新 REPL 中注册的自定义命令
import _pyrepl.commands as commands

# 列出所有已注册的命令名
print(sorted(commands.commands))
# 包含诸如 'exit'、'quit'、'help' 等条目

需要注意:_pyrepl 属于实现细节,其内部结构可能在不同版本间调整。日常使用无需关心这些细节,了解其存在即可——它是「为何 exit 不加括号也能工作」的答案。

向后兼容性 ​

PEP 762 明确要求保持向后兼容,具体体现为:

  • 语言语义与脚本执行行为完全不变;
  • >>> 与 ... 提示符形式保持延续;
  • 旧的 readline 配置(PYTHONSTARTUP、历史文件)仍被尊重;
  • 提供 PYTHON_BASIC_REPL=1 作为回退开关。

因此,升级到 3.13 不会破坏现有脚本或依赖 readline 的既有配置。唯一的差异是交互体验层面的增强。

语法高亮与提示符着色 ​

新 REPL 在输入时实时着色,着色的对象包括:

元素说明
关键字def、return、import 等
字符串按引号类型区分
注释以 # 开头的内容
数字与常量字面量高亮
提示符>>> 与 ... 可着色

若终端不支持颜色,或设置了 NO_COLOR,高亮会自动降级为无着色文本,功能不受影响。

终端能力探测 ​

新 REPL 依赖终端支持 ANSI 转义序列与一定的键位报告能力。在启动时会做探测:

  • 若 TERM 为 dumb 或缺省,通常回退到基本模式;
  • 若 stdin 不是 TTY,直接回退;
  • Windows 上 3.13 起的新 REPL 需要现代终端(Windows Terminal 等)。
bash
# 查看当前终端类型
echo "$TERM"
# xterm-256color

# 确认标准输入是否为终端
test -t 0 && echo "可交互" || echo "非终端,将回退旧 REPL"

打印欢迎横幅与退出信息 ​

新 REPL 启动时会显示类似下面的横幅:

text
Python 3.13.1 (main, Dec  3 2024, 10:00:00) [Clang 16.0.0] on darwin
Type "help", "copyright", "credits" or "license" for more information.

这与旧 REPL 相同。区别在于退出时,新 REPL 在收到 EOF 或 exit 后可能有短暂的清理过程,属于正常现象。

实战示例 ​

示例一:在 REPL 中编辑多行函数 ​

新 REPL 支持回溯编辑已输入的行,这是旧版做不到的。

text
>>> def greet(name: str) -> str:
...     """返回问候语。"""
...     return f"Hello, {name}!"
...
>>> greet("World")
'Hello, World!'

若发现拼写错误,在按空行执行之前,可以用方向键上移光标,直接修改 return 那一行,再回车执行。旧 REPL 中你只能按 Ctrl-C 全部重来。

示例二:观察彩色回溯 ​

写一段必然出错的代码,比较彩色开关的差异:

python
# demo_traceback.py
def divide(a: int, b: int) -> float:
    """执行除法,b 为 0 时抛出异常。"""
    return a / b


if __name__ == "__main__":
    print(divide(10, 0))
bash
# 默认:回溯中的文件、行号、代码行着色
python demo_traceback.py

# 关闭颜色
NO_COLOR=1 python demo_traceback.py

在 3.13 中,回溯里的 demo_traceback.py、行号 9、以及 print(divide(10, 0)) 这一行都会用不同颜色标出,异常类型与消息也会加粗或着色,定位更快。

示例三:用脚本判断当前 REPL 能力 ​

下面的脚本在运行时判断当前解释器是否具备新 REPL,便于编写跨版本的开发辅助脚本。

python
# repl_capabilities.py
from __future__ import annotations

import importlib.util
import sys


def has_new_repl() -> bool:
    """判断当前解释器是否内置 3.13 的新 REPL。"""
    if sys.version_info < (3, 13):
        return False
    return importlib.util.find_spec("_pyrepl") is not None


def describe() -> dict[str, object]:
    """汇总当前交互环境的能力信息。"""
    return {
        "python": sys.version.split()[0],
        "new_repl": has_new_repl(),
        "basic_repl_forced": bool(sys.flags.interactive and not has_new_repl()),
        "supports_f1_help": has_new_repl(),
        "supports_history_browser": has_new_repl(),
    }


if __name__ == "__main__":
    for key, value in describe().items():
        print(f"{key:26} {value}")

若输出中 new_repl 为 False,说明当前环境回退到了旧实现,可检查 PYTHON_BASIC_REPL 是否被设置。

示例四:在 Shell 中按需切换两套 REPL ​

在同一个系统里同时保留新、旧两套体验,用于排查终端兼容问题:

bash
# 默认使用新 REPL
python

# 临时回退到旧 REPL,对比同一段代码的体验
PYTHON_BASIC_REPL=1 python

# 在 CI 或日志环境中强制关闭颜色,避免转义序列污染日志
NO_COLOR=1 python -c "print('done')"

把这几条命令记录成 Shell 别名,可以在遇到终端问题时快速切换定位。

注意事项 ​

  • 新 REPL 需要 TTY:管道输入或重定向输入时会自动回退,功能不可用属于预期行为。
  • 不是所有终端都完全兼容:极简终端或旧版终端复用器可能显示异常,此时可用 PYTHON_BASIC_REPL=1 回退。
  • exit/quit 直接调用仅限 3.13+:跨版本脚本不要依赖这一行为。
  • 颜色受终端能力影响:把输出重定向到文件时颜色会默认关闭,需要颜色请显式设 FORCE_COLOR。
  • F1 帮助浏览器与 help() 不是一回事:前者是持续浏览界面,后者是分页输出。
  • 新 REPL 不影响脚本执行:它只改变交互体验,不改变语言语义,因此不会让脚本行为发生变化。
  • 不要依赖 REPL 内部实现:_pyrepl 属于实现细节,名称与行为可能在未来版本调整。

常见问题 ​

Q:为什么我的 3.13 没有彩色提示符? 可能是终端不支持,或设置了 NO_COLOR/PYTHON_COLORS=0。先确认环境变量,再确认终端本身能显示 ANSI 颜色。

Q:新 REPL 和 IPython 冲突吗? 不冲突,二者是独立的交互环境。日常用内置 REPL,需要富输出与魔术命令时启动 IPython。

Q:F1/F2/F3 在某些键盘上没有? 笔记本电脑可能将 F 键映射为多媒体键,需要配合 Fn 键按下。

Q:能否自定义新 REPL 的提示符颜色? 可以,通过 sys.ps1/sys.ps2 与颜色转义序列实现,详见 启动文件与配置。

Q:新 REPL 支持自动补全吗? 内置实现提供基础的补全支持,但功能弱于 IPython,可按需自定义补全器,详见 颜色高亮与自动补全。

Q:为什么 pip install readline 在新 REPL 上没效果? 新 REPL 不再以 readline 为后端,绝大部分行编辑功能由 PyREPL 自行实现,因此增强 readline 不会改变新 REPL 的行为。

Q:多行粘贴的时候代码被拆开了怎么办? 先按 F3 进入粘贴模式,粘贴完成后再按一次 F3 返回常规提示符。或者改用 python -c / 脚本文件方式执行。

Q:sys.ps1 在新 REPL 里还是 '>>> ' 吗? 是的,提示符变量仍然存在且默认值不变,方便既有配置继续工作。

Q:能否在 VS Code 等编辑器里用上这套 REPL? 编辑器通常使用自己的交互窗口,不一定复用终端 REPL。若希望体验新 REPL,可在编辑器的集成终端中直接运行 python。

最佳实践 ​

  1. 升级到 Python 3.13+ 后优先体验新 REPL,多行编辑与历史浏览器能显著提升效率。
  2. 在日志与 CI 环境中设置 NO_COLOR=1,避免颜色转义码污染日志。
  3. 遇到终端兼容问题时先用 PYTHON_BASIC_REPL=1 回退定位,而不是急着换工具。
  4. 用 F2 替代反复按方向键,尤其在输出很多的会话里。
  5. 粘贴大段代码前先按 F3 进入粘贴模式,避免被逐行提前执行。
  6. 跨版本兼容的脚本应通过 importlib.util.find_spec("_pyrepl") 探测能力,而非硬编码版本号。
  7. 需要富交互能力(图表、魔术命令)时,仍选择 IPython,两者互补。

下一节

继续学习:多行编辑与历史记录

参考链接 ​