MicroPython 交互式解释器模式(即 REPL)

本节介绍 MicroPython 交互式解释器模式的一些特性。它常被称为 REPL(read-eval-print-loop,读取-求值-打印循环),后文将用该名称指代这一交互式提示符。

备注

OpenMV Cam 通过其 USB 串口(CDC)暴露此 REPL。它仅在你使用串口终端模拟器(或诸如 MicroPython 远程控制:mpremote 之类的工具)直接连接摄像头时才相关。OpenMV IDE 并不使用 REPL —— 它通过一套独立的调试协议与摄像头通信,以运行脚本、传输文件并串流帧缓冲区。本页所述内容仅适用于直接的终端会话。

自动缩进

当你输入以冒号结尾的 Python 语句(例如 if、for、while)时,提示符会变为三个点(...),光标会缩进 4 个空格。当你按下回车时,对于普通语句,下一行会继续保持相同的缩进级别,而在适当的情况下则会再增加一级缩进。如果你按下退格键,则会撤销一级缩进。

如果光标已经回到行首,此时按回车将执行你已输入的代码。下面展示了输入一条 for 语句后你会看到的内容(下划线表示光标最终所在的位置):

>>> for i in range(30):
...     _

如果你接着输入一条 if 语句,将会再提供一级缩进:

>>> for i in range(30):
...     if i > 3:
...         _

现在输入 break 并按回车,再按退格键:

>>> for i in range(30):
...     if i > 3:
...         break
...     _

最后输入 print(i),按回车,再按退格,然后再次按回车:

>>> for i in range(30):
...     if i > 3:
...         break
...     print(i)
...
0
1
2
3
>>>

如果前两行都是空格,则不会应用自动缩进。这意味着你可以通过按两次回车来结束一条复合语句的输入,第三次按回车则会完成并执行。

自动补全

在 REPL 中输入命令时,如果目前已输入的内容对应于某个名称的开头,那么按 TAB 键将显示所有可能输入的内容。例如,先通过输入 import machine 并按回车来导入 machine 模块。然后输入 m 并按 TAB,它应当扩展为 machine。输入一个点 . 并再次按 TAB,你应当会看到类似如下的内容:

>>> machine.
__name__        info            unique_id       reset
bootloader      freq            rng             idle
sleep           deepsleep       disable_irq     enable_irq
Pin

该词将被尽可能地扩展,直到存在多种可能为止。例如,输入 machine.Pin.PULL 并按 TAB,它会扩展为 machine.Pin.PULL_。再按一次 TAB 将显示所有可能的扩展:

>>> machine.Pin.PULL_
PULL_DOWN       PULL_UP
>>> machine.Pin.PULL_

中断正在运行的程序

你可以通过按 Ctrl-C 来中断正在运行的程序。这将引发 KeyboardInterrupt,使你回到 REPL,前提是你的程序没有拦截 KeyboardInterrupt 异常。

例如:

>>> for i in range(1000000):
...     print(i)
...
0
1
2
3
...
6466
6467
6468
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyboardInterrupt:
>>>

粘贴模式

如果你想把一些代码粘贴到终端窗口中,自动缩进功能会把内容弄乱。例如,假设你有如下 Python 代码:

def foo():
    print('This is a test to show paste mode')
    print('Here is a second line')
foo()

而你试图将它粘贴到普通的 REPL 中,那么你会看到类似这样的内容:

>>> def foo():
...         print('This is a test to show paste mode')
...             print('Here is a second line')
...             foo()
...
Traceback (most recent call last):
  File "<stdin>", line 3
IndentationError: unexpected indent

如果你按下 Ctrl-E,就会进入粘贴模式,它实质上关闭了自动缩进功能,并将提示符从 >>> 变为 ===。例如:

>>>
paste mode; Ctrl-C to cancel, Ctrl-D to finish
=== def foo():
===     print('This is a test to show paste mode')
===     print('Here is a second line')
=== foo()
===
This is a test to show paste mode
Here is a second line
>>>

粘贴模式允许粘贴空行。粘贴的文本会像文件一样被编译。按 Ctrl-D 退出粘贴模式并开始编译。

软复位

软复位 会重置 Python 解释器,但会尽量不重置你连接 OpenMV Cam 所用的方式(USB)。

你可以在 REPL 中按 Ctrl-D 来执行软复位,或者在 Python 代码中执行如下语句:

machine.soft_reset()

例如,如果你复位 OpenMV Cam 并执行 dir() 命令,你会看到类似这样的内容:

>>> dir()
['__name__']

现在创建一些变量并重复执行 dir() 命令:

>>> i = 1
>>> j = 23
>>> x = 'abc'
>>> dir()
['j', 'x', '__name__', 'i']
>>>

现在如果你按下 Ctrl-D,并重复执行 dir() 命令,你会看到你的变量已不再存在:

MPY: sync filesystems
MPY: soft reboot
MicroPython v1.25.0 on 2025-05-15; OpenMV Cam H7 with STM32H743
Type "help()" for more information.
>>> dir()
['__name__']
>>>

有关复位类型和启动过程的更多信息,请参阅 复位与启动序列

特殊变量 _(下划线)

当你使用 REPL 时,你可能会进行一些计算并查看结果。MicroPython 会把上一条语句的结果存储在变量 _(下划线)中。因此你可以使用下划线把结果保存到一个变量中。例如:

>>> 1 + 2 + 3 + 4 + 5
15
>>> x = _
>>> x
15
>>>

原始模式与原始粘贴模式

原始模式(也称为 raw REPL)通常不是供人直接使用的。它面向程序化使用,本质上其行为类似于关闭了回显的粘贴模式,并带有可选的流控制。

通过 Ctrl-A 进入原始模式。然后发送你的 Python 代码,接着发送 Ctrl-D。设备会以 'OK' 来确认 Ctrl-D,随后会编译并执行该 Python 代码。任何输出(或错误)都会被发回。按 Ctrl-B 将离开原始模式并返回常规(即友好)REPL。

原始粘贴模式是 raw REPL 中的一种附加模式,它包含流控制,并且在接收代码的同时进行编译。这使得它在向设备高速传输代码时更为稳健,并且在接收时占用更少的 RAM,因为它无需在编译前存储一份代码的逐字副本(这与标准原始模式不同)。

原始粘贴模式使用以下协议:

  1. 像往常一样通过 ctrl-A 进入 raw REPL。

  2. 写入 3 个字节:b"\x05A\x01"(即 ctrl-E,然后是 "A",再是 ctrl-A)。

  3. 读取 2 个字节以确定设备是否进入了原始粘贴模式:

    • 如果结果是 b"R\x00",则设备能理解该命令但不支持原始粘贴。

    • 如果结果是 b"R\x01",则设备确实支持原始粘贴并已进入此模式。

    • 否则结果应为 b"ra",表示设备不支持原始粘贴,此时应读取并丢弃字符串 b"w REPL; CTRL-B to exit\r\n>"

  4. 如果设备处于原始粘贴模式则继续,否则回退到标准原始模式。

  5. 读取 2 个字节,这是流控制的窗口大小增量(以字节为单位),存储为 16 位无符号小端整数。剩余窗口大小变量的初始值应设为该数值。

  6. 将代码写出到设备:

    • 只要还有字节要发送,就写入最多剩余窗口大小数量的字节,并将剩余窗口大小减去已写入的字节数。

    • 如果剩余窗口大小为 0,或者有一个字节等待读取,则读取 1 个字节。如果该字节是 b"\x01",则将剩余窗口大小增加第 5 步中的窗口大小增量。如果该字节是 b"\x04",则表示设备希望结束数据接收,此时应向设备写入 b"\x04",之后不再发送任何代码。(注意:如果有一个字节等待从设备读取,无需立即读取并响应它,只要剩余窗口大小大于 0,设备就会持续消费传入的字节。)

  7. 当所有代码都已写出到设备后,写入 b"\x04" 以表示数据结束。

  8. 从设备读取,直到收到 b"\x04"。此时设备已接收并编译了所发送的全部代码,并正在执行它。

  9. 设备会输出执行代码所产生的任何字符。当(如果)代码执行完毕时,将输出 b"\x04",随后是任何未捕获的异常,再之后又是 b"\x04"。然后它会返回标准 raw REPL 并输出 b">"

例如,在正常(友好)REPL 中从新的一行开始,如果你写入:

b"\x01\x05A\x01print(123)\x04"

那么设备将以类似如下的内容作出响应:

b"\r\nraw REPL; CTRL-B to exit\r\n>R\x01\x80\x00\x01\x04123\r\n\x04\x04>"

按时间分解开来,看起来是这样的:

# Step 1: enter raw REPL
write: b"\x01"
read: b"\r\nraw REPL; CTRL-B to exit\r\n>"

# Step 2-5: enter raw-paste mode
write: b"\x05A\x01"
read: b"R\x01\x80\x00\x01"

# Step 6-8: write out code
write: b"print(123)\x04"
read: b"\x04"

# Step 9: code executes and result is read
read: b"123\r\n\x04\x04>"

在此例中,流控制的窗口大小增量为 128,且在开始时立即有两个窗口大小的数据可用,一个来自初始的窗口大小增量值,另一个来自发送的显式 b"\x01" 值。因此这意味着一开始最多可写入 256 个字节,然后再等待或检查更多传入的流控制字符。

MicroPython 远程控制:mpremote 工具使用 raw REPL(包括原始粘贴模式)在 OpenMV Cam 上执行 Python 代码。