Skip to content

REPL 基础操作 ​

REPL 是 Read-Eval-Print Loop(读取-求值-打印循环)的缩写,指 Python 解释器提供的交互式命令行环境。你输入一行代码,解释器立刻执行并把结果打印出来,再等待下一次输入——这个循环让 REPL 成为学习语法、验证想法、调试片段最直接的场所。

前置知识

阅读本节前,建议先了解:Python 版本生命周期

基础概念 ​

在终端输入 python(Windows 上可能是 py),若未指定脚本文件,解释器会进入交互模式,显示主提示符 >>>:

text
$ python
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 的三个阶段:

阶段行为
Read读取你输入的一行(或一个完整语句块)
Eval编译并执行这段代码
Print若不是 None,把表达式结果用 repr() 打印出来

理解「Print 使用 repr」很重要:在 REPL 中直接输入 'hello' 会显示带引号的 'hello',而 print('hello') 显示不带引号的 hello。前者展示对象的规范表示,后者展示面向人的字符串。

REPL 与「直接运行脚本」的关键差异:

维度REPL脚本文件
执行方式逐条语句即时执行整个文件从头到尾
结果输出自动打印表达式结果只有 print 等显式输出
状态变量在会话内持续存在每次运行重新开始
适用场景探索、验证、调试交付、自动化、复用

需要提醒的是:REPL 是「探索工具」,不是「写代码的地方」。在 REPL 里试通一段逻辑后,应及时复制到 .py 文件中保存,否则关闭终端就全部丢失。

语法与用法 ​

启动 REPL 有多种方式:

bash
# 直接启动(若系统同时装了 Python 2 和 3,需用 python3)
python

# 明确指定版本
python3.13

# 使用版本管理器提供的版本
pyenv shell 3.13.1 && python
uv run python

# 启动后立即执行一段代码,再进入交互模式(-i 的用途)
python -i script.py

# 只执行一段代码,不进入 REPL
python -c "print(1 + 1)"

# 启动时不加载 site 模块(可加快启动,但会缺少部分功能)
python -S

退出 REPL 有几种方式:

text
# 方式一:内置函数(Python 3.13+ 可直接写 exit 或 quit,无需括号)
exit
quit

# 方式二:显式调用(所有版本通用)
exit()
quit()

# 方式三:键盘快捷键
Ctrl-D     # Unix/macOS,发送 EOF
Ctrl-Z 回车 # Windows

# 方式四:导入 sys 后调用
import sys; sys.exit()

在 REPL 中执行常见操作:

python
>>> 1 + 2                     # 表达式结果自动打印
3

>>> name = "Python"           # 赋值语句无输出
>>> name                      # 查看变量用 repr
'Python'

>>> print(name)               # print 输出 str
Python

>>> _                         # 下划线保存上一个表达式的结果
'Python'

>>> 3 * 7
21
>>> _ + 1                     # 基于上一个结果继续计算
22

>>> type(1 + 2)
<class 'int'>

多行语句会触发续行提示符:

python
>>> for i in range(3):
...     print(i)
...
0
1
2

>>> def add(a: int, b: int) -> int:
...     """返回两数之和。"""
...     return a + b
...
>>> add(3, 4)
7

注意代码块结束后需要输入一个空行,REPL 才知道语句块已经输完。

使用内置帮助工具:

python
>>> help(str)                 # 查看类型的文档(进入分页浏览,按 q 退出)
>>> help(str.split)           # 查看具体方法
>>> dir(str)                  # 列出类型的属性与方法名
>>> str.split?                # 在 IPython/Jupyter 中才有此语法,原生 REPL 不支持

详细说明 ​

REPL 的执行流程 ​

输入 >>> print(1 + 1) 后,解释器内部依次做了这些事:

  1. 读取这一行文本;
  2. 用 compile(..., mode='single') 尝试编译;
  3. 若抛出 SyntaxError 且原因是「输入不完整」,则显示 ... 继续读下一行;
  4. 编译成功后执行字节码;
  5. 若编译模式为 single 且结果不是 None,用 sys.displayhook 打印其 repr。

sys.displayhook 是可以替换的,这也是 IPython 等增强型 REPL 显示富文本输出的原理。

python
>>> import sys
>>> sys.displayhook
<built-in function displayhook>

# 查看提示符变量(交互模式下才存在)
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '

sys.ps1/sys.ps2 只在交互模式下定义,脚本中访问会抛 AttributeError。修改它们可以自定义提示符(见 启动文件与配置)。

判断代码是否运行在 REPL 中 ​

有些代码需要区分「交互运行」与「脚本运行」。常用手段:

方式说明
sys.flags.interactive是否为 -i 启动的交互模式
hasattr(sys, "ps1")是否处于真正的交互式 REPL
__name__ == "__main__"是否作为主程序运行(不区分交互/脚本)
sys.ps1 访问仅在交互模式存在,否则抛异常

其中 hasattr(sys, "ps1") 是最可靠的「是否在 REPL 中」判断:

python
import sys


def in_repl() -> bool:
    """判断当前是否运行在交互式解释器中。"""
    return hasattr(sys, "ps1")


if __name__ == "__main__":
    if in_repl():
        print("正在 REPL 中运行")
    else:
        print("正在以脚本方式运行")

python -i:执行后进入 REPL ​

-i 参数的用途是:先运行指定脚本,运行结束后不退出,而是进入交互模式,并保留脚本中定义的所有变量。

bash
# run_then_probe.py
python -i run_then_probe.py
python
# run_then_probe.py
def load_config(path: str) -> dict[str, str]:
    """模拟加载配置,返回键值对。"""
    return {"host": "localhost", "port": "8000"}


config = load_config("app.toml")
print("配置加载完成,可在 REPL 中查看变量 config")

运行后进入 >>>,可以直接输入 config 查看结果,非常适合作「加载数据后手工探查」的调试方式。脚本异常退出时也会保留现场,便于检查出错前的状态。

python -c:单行执行 ​

-c 适合在 Shell 脚本中嵌入简短逻辑,不进入 REPL:

bash
# 打印版本号
python -c "import sys; print(sys.version)"

# 使用命令行参数(注意 sys.argv[0] 为 -c)
python -c "import sys; print(sys.argv[1:])" alpha beta
# ['alpha', 'beta']

# 计算并输出
python -c "print(sum(range(101)))"
# 5050

-c 后可以跟多条语句,用分号或换行分隔;但它不适合放复杂逻辑——超过一两行就该写成脚本文件。

常用魔术变量与对象 ​

原生 REPL 提供几个便利对象:

名称含义
_上一个表达式的结果
__name__交互模式下为 "__main__"
__doc__当前模块的文档字符串
help_sitebuiltins._Helper 实例
exit / quit_sitebuiltins.Quitter 实例

_ 只在交互模式下由 sys.displayhook 自动赋值:

python
>>> 10 / 4
2.5
>>> round(_, 0)
2.0
>>> _ * 2
4.0

注意 _ 不会在脚本中自动赋值,它是交互模式的专属便利。

用 dir() 与 help() 探索对象 ​

REPL 最大的价值之一是「就地探索」。不必离开终端去查文档:

python
>>> import json
>>> dir(json)                       # 列出模块公开成员
['JSONDecodeError', 'JSONDecoder', ..., 'dumps', 'loads']

>>> help(json.loads)                # 查看函数签名与文档
Help on function loads in module json:
loads(s, *, cls=None, ...)

>>> json.dumps({"a": 1}, ensure_ascii=False)
'{"a": 1}'

>>> s = "  hello  "
>>> dir(s)                          # 实例也可列出可用方法

>>> help(s.strip)

对于自带文档字符串的函数,help() 会直接展示。若文档是分页显示的,按 q 退出、空格翻页、回车下一行。

实战示例 ​

示例一:用 REPL 快速验证字符串处理 ​

假设要从一段日志中提取时间戳,先在 REPL 中反复试验,确认正则可行后再落盘。

python
>>> line = "2024-10-07 12:34:56 ERROR failed to connect"
>>> parts = line.split(maxsplit=1)
>>> parts
['2024-10-07', '12:34:56 ERROR failed to connect']

发现 split 只能切一次,改用正则:

python
>>> import re
>>> pattern = re.compile(r"^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) (\w+) (.*)$")
>>> match = pattern.match(line)
>>> match.groups()
('2024-10-07', '12:34:56', 'ERROR', 'failed to connect')

>>> # 验证不匹配时的行为
>>> pattern.match("invalid line") is None
True

确认无误后归档为脚本:

python
# parse_log.py
import re

LOG_PATTERN = re.compile(r"^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) (\w+) (.*)$")


def parse_line(line: str) -> dict[str, str] | None:
    """解析单行日志,失败返回 None。"""
    match = LOG_PATTERN.match(line)
    if match is None:
        return None
    date, time_, level, message = match.groups()
    return {"date": date, "time": time_, "level": level, "message": message}


if __name__ == "__main__":
    sample = "2024-10-07 12:34:56 ERROR failed to connect"
    print(parse_line(sample))

这正是 REPL 的典型用法:先在交互环境试错,再把稳定的逻辑写成函数并加类型标注。

示例二:用 python -i 调试数据加载流程 ​

当一段数据处理逻辑出错时,可以在脚本末尾留一个探针,配合 -i 检查中间状态。

python
# load_records.py
from __future__ import annotations


def load_records() -> list[dict[str, object]]:
    """模拟从数据源加载记录。"""
    return [
        {"id": 1, "name": "alpha", "score": 90},
        {"id": 2, "name": "beta", "score": None},
        {"id": 3, "name": "gamma", "score": 75},
    ]


def average_score(records: list[dict[str, object]]) -> float:
    """计算平均分,跳过缺失分数的记录。"""
    scores = [r["score"] for r in records if r["score"] is not None]
    return sum(scores) / len(scores)  # type: ignore[arg-type]


records = load_records()
print(average_score(records))
bash
$ python -i load_records.py
82.5
>>> records[1]                    # 探查中间数据
{'id': 2, 'name': 'beta', 'score': None}
>>> [r for r in records if r["score"] is None]
[{'id': 2, 'name': 'beta', 'score': None}]
>>> average_score([{"score": 1}])
1.0

用这种方式,不需要反复加 print 再重跑脚本,直接在会话里试验不同输入。

示例三:在 REPL 中验证「表达式结果与 print 结果」的差异 ​

理解 repr 与 str 的区别对写文档示例与测试断言很关键:

python
>>> value = "tab\there"
>>> value                 # repr:转义可见
'tab\there'
>>> print(value)          # str:转义生效
tab	here

>>> items = ["a", "b"]
>>> items                 # repr 展示结构
['a', 'b']
>>> print(items)          # str 展示可读形式
['a', 'b']

>>> from decimal import Decimal
>>> Decimal("1.10")       # repr 保留有效位
Decimal('1.10')
>>> print(Decimal("1.10"))
1.10

>>> class Point:
...     def __init__(self, x: int, y: int) -> None:
...         self.x = x
...         self.y = y
...     def __repr__(self) -> str:
...         return f"Point(x={self.x!r}, y={self.y!r})"
...
>>> Point(1, 2)
Point(x=1, y=2)

当自定义类的 __repr__ 设计得当时,在 REPL 中探查对象会非常直观——这也是官方推荐「__repr__ 应尽量返回可重建对象的表达式」的原因。

注意事项 ​

  • python 可能指向 Python 2:在老系统中 python 命令可能仍是 Python 2,应使用 python3 或版本管理器明确指定。
  • REPL 中的代码不会被保存:关掉终端即丢失,重要的逻辑必须及时写入文件。
  • _ 只在交互模式自动赋值:脚本里使用 _ 需要自己定义,否则会 NameError。
  • 代码块需以空行结束:输入 for、def、if 等复合语句后,要再按一次回车(空行)才会执行。
  • 缩进必须一致:REPL 中混用 Tab 与空格会报 IndentationError,建议统一 4 空格。
  • 不要用 REPL 跑长时间任务:无法中断后恢复,误关终端会前功尽弃;长任务应写成脚本并支持断点续跑。
  • exit/quit 在脚本中不可用:它们是 site 模块注入的交互专用对象,脚本中应使用 sys.exit()。
  • -c 的引号由 Shell 解析:外层引号选择不当会被 Shell 抢先替换变量,注意转义。

最佳实践 ​

  1. 用 python3 或版本管理器明确指定解释器,避免误入 Python 2。
  2. 把 REPL 当作草稿纸:先在这里试通,再整理成带类型标注的函数保存到文件。
  3. 给自定义类实现 __repr__,让 REPL 中的探查结果清晰可读。
  4. 用 python -i script.py 加载数据后进入交互,替代反复插 print 的调试方式。
  5. 用 dir() 和 help() 就地探索陌生模块,减少切换文档的成本。
  6. 理解 repr 与 str 的差异,写测试断言时优先关注 repr 输出的确定性。
  7. 需要更顺手的交互体验时,升级到 Python 3.13+ 的新 REPL(见下一节)或使用 IPython。

下一节

继续学习:Python 3.13 改进的 REPL

参考链接 ​