io --- 输入/输出流

本模块包含其他类型的 stream(类文件)对象以及辅助函数。它对外提供内建的 open() 函数,以及实现了标准 read/write/seek 流接口的内存中文本和二进制缓冲区(StringIOBytesIO)。

概念层次结构

与 CPython 的差异

如本节所述,MicroPython 简化了流基类的概念层次结构。

(抽象)基类流,作为所有具体类行为的基础,在 CPython 中遵循若干二分法(成对分类)。在 MicroPython 中,为了实现更高的效率并节省资源,这些分类被有所简化并隐式化处理。

CPython 中一个重要的二分法是无缓冲流与有缓冲流。在 MicroPython 中,目前所有的流都是无缓冲的。这是因为所有现代操作系统,乃至许多 RTOS 和文件系统驱动程序,都已经在它们这一侧进行了缓冲。再增加一层缓冲会适得其反(这一问题被称为“缓冲膨胀”,即 "bufferbloat"),还会占用宝贵的内存。请注意,仍然存在缓冲可能有用的情况,因此我们可能会在以后引入可选的缓冲支持。

但在 CPython 中,另一个重要的二分法与“是否带缓冲”相关——即一个流是否可能发生短读/短写。短读是指用户向流请求例如 10 个字节,但得到的字节数更少,短写与此类似。在 CPython 中,无缓冲流自动具有可能发生短操作的特性,而有缓冲流则保证不会发生短操作。无短读/短写是一项重要的特性,因为它使得开发出更简洁高效的程序成为可能——这对 MicroPython 而言非常理想。因此,虽然 MicroPython 不支持有缓冲流,但它仍然提供无短操作的流。是否会发生短操作取决于每个具体类的需求,但出于上述原因,强烈建议开发者优先采用无短操作的行为。例如,MicroPython 的套接字保证避免短读/短写。实际上,目前在核心代码中还没有短操作流类的实例,这样的类应当是针对特定硬件的。

在非阻塞流的情况下,无短操作的行为变得棘手,阻塞与非阻塞行为是 CPython 的另一个二分法,MicroPython 对其提供了完整支持。非阻塞流从不等待数据到达或被写入——它们读取/写入一切可能的内容,或者发出缺乏数据(或缺乏写入数据能力)的信号。显然,这与“无短操作”策略相冲突,事实上,非阻塞且有缓冲(也即无短操作)的流在 CPython 中是一种复杂纠结的情形——在某些地方禁止这种组合,在某些地方它是未定义的或只是未被记录,在某些情况下它会引发冗长的异常。这一问题在 MicroPython 中要简单得多:非阻塞流对于高效的异步操作非常重要,因此该特性优先于“无短操作”特性。所以,虽然阻塞流会尽可能避免短读/短写(唯一会发生短读的情况是到达文件末尾,或者发生错误时(但错误不会返回短数据,而是引发异常)),非阻塞流则可能产生短数据以避免阻塞操作。

最后一个二分法是二进制流与文本流。MicroPython 当然支持这两者,但在 CPython 中文本流本质上是带缓冲的,而在 MicroPython 中它们不带缓冲。(事实上,这正是我们可能引入缓冲支持的情形之一。)

请注意,出于效率考虑,MicroPython 不提供与上述层次结构对应的抽象基类,也无法在纯 Python 中实现或派生子类化一个流类。

函数

io.open(name: str, mode: str = 'r', **kwargs) Any

打开一个文件。内建的 open() 函数是本函数的别名。mode 参数始终受支持;对其他参数的支持情况可能有所不同。

class io.IOBase

流(“类文件”)对象的基类。具体的子类实现下面的底层 I/O 方法(readintowriteioctl);运行时在它们之上构建更高层的流协议(readreadlinereadlinesclose、迭代),因此即使子类没有定义这些方法,每个流实例也都支持它们。

实现方法(在子类中重写这些方法):

readinto(buf: bytearray) int | None

将字节读入可写缓冲区 buf。返回读取的字节数,到达流末尾时返回 0,如果当前没有可用数据(对于非阻塞流)则返回 None

write(buf: bytes) int | None

写入 buf 中的字节。返回写入的字节数,如果当前无法执行写入(对于非阻塞流)则返回 None

ioctl(request: int, arg: int) int

控制底层流/设备。requestMP_STREAM_* 请求码之一。成功时返回非负值,出错时返回负的 errno 值。

流协议方法(在每个流实例上均可用):

read(size: int = -1)

读取并返回最多 size 个字节(在文本模式下为字符)。如果省略 size 或其为负值,则一直读取到流末尾。对于二进制流返回 bytes,对于文本流返回 str;空结果表示已到达流末尾。

readline(size: int = -1)

读取并返回一行,如果存在末尾换行符则包含该换行符。如果给定了 size,则最多读取 size 个字节(或字符)。到达流末尾时返回空的 bytes / str

readlines() list

读取直到流末尾,并返回一个由各行组成的 list,每行都带有其末尾的换行符。

close() None

关闭流并释放所有底层资源。对已关闭的流进行操作会引发 OSError(对于内存中的流则引发 ValueError)。

seek(offset: int, whence: int = 0) int

将当前流位置更改为相对于 whenceoffset 个字节(0 = 流起始处,1 = 当前位置,2 = 流末尾)。返回新的绝对位置。在不可寻址的流上引发 OSError

tell() int

返回流中当前的绝对位置。等同于 seek(0, 1)

flush() None

刷新所有写缓冲区,将待处理的数据推送到底层设备或文件。对于不进行缓冲的流,此操作无任何效果。

直接迭代一个流会在每次迭代中产生一行——等同于在循环中调用 readline(),直到返回表示流末尾的空行哨兵值。流还支持上下文管理器协议,因此 with open(...) as f: 会自动关闭流。

备注

MicroPython 的流模块还对外提供以 “1” 为后缀的 C 辅助函数 mp_stream_read1_objmp_stream_readinto1_objmp_stream_write1_obj,它们执行单次底层 I/O 调用,而不是循环直到请求被完全满足。诸如 machine.UART 之类的类在内部使用它们来实现自己的 read / write——但没有任何标准流类将它们绑定为可从 Python 调用的 read1 / readinto1 / write1 方法。

class io.StringIO(string: str = '')

用于文本模式输入/输出的内存中类文件对象(类似于以 "t" 修饰符打开的普通文件)。初始内容可以通过 string 参数指定(该参数应为普通字符串)。实例也支持上下文管理器协议(可在 with 语句中使用)。

read(size: int = -1) str

读取并返回最多 size 个字符。如果省略 size 或其为负值,则读取并返回所有剩余内容。

readline(size: int = -1) str

读取并返回一行。如果给定了 size,则最多读取 size 个字符。

readinto(buf: bytearray) int

读入预先分配的可写缓冲区 buf,并返回读取的字节数。

write(s: str) int

写入字符串 s 并返回写入的字符数。

seek(offset: int, whence: int = 0) int

将流位置更改为相对于 whenceoffset0 = 起始处,1 = 当前位置,2 = 末尾),并返回新的绝对位置。

tell() int

返回当前的流位置。

flush() None

刷新写缓冲区。对于内存中的流,此操作无任何效果。

close() None

关闭流并释放底层缓冲区。对已关闭的流进行进一步操作会引发 ValueError

getvalue() str

返回底层缓冲区当前的内容。

class io.StringIO(alloc_size: int)

创建一个空的 StringIO 对象,预先分配可容纳最多 alloc_size 个字节的空间,这样写入最多这么多字节时不会重新分配缓冲区(从而避免内存不足的情况或内存碎片化)。此构造函数是 MicroPython 的扩展,仅推荐用于特殊场景和系统级库,不适用于最终用户应用程序。

与 CPython 的差异

此构造函数是 MicroPython 的扩展。

read(size: int = -1) str

读取并返回最多 size 个字符。如果省略 size 或其为负值,则读取并返回所有剩余内容。

readline(size: int = -1) str

读取并返回一行。如果给定了 size,则最多读取 size 个字符。

readinto(buf: bytearray) int

读入预先分配的可写缓冲区 buf,并返回读取的字节数。

write(s: str) int

写入字符串 s 并返回写入的字符数。

seek(offset: int, whence: int = 0) int

将流位置更改为相对于 whenceoffset0 = 起始处,1 = 当前位置,2 = 末尾),并返回新的绝对位置。

tell() int

返回当前的流位置。

flush() None

刷新写缓冲区。对于内存中的流,此操作无任何效果。

close() None

关闭流并释放底层缓冲区。对已关闭的流进行进一步操作会引发 ValueError

getvalue() str

返回底层缓冲区当前的内容。

class io.BytesIO(string: bytes = b'')

用于二进制模式输入/输出的内存中类文件对象(类似于以 "b" 修饰符打开的普通文件)。初始内容可以通过 string 参数指定(该参数应为一个 bytes 对象)。实例也支持上下文管理器协议(可在 with 语句中使用)。

read(size: int = -1) bytes

读取并返回最多 size 个字节。如果省略 size 或其为负值,则读取并返回所有剩余内容。

readline(size: int = -1) bytes

读取并返回一行。如果给定了 size,则最多读取 size 个字节。

readinto(buf: bytearray) int

读入预先分配的可写缓冲区 buf,并返回读取的字节数。

write(b: bytes) int

写入类字节对象 b 并返回写入的字节数。

seek(offset: int, whence: int = 0) int

将流位置更改为相对于 whenceoffset0 = 起始处,1 = 当前位置,2 = 末尾),并返回新的绝对位置。

tell() int

返回当前的流位置。

flush() None

刷新写缓冲区。对于内存中的流,此操作无任何效果。

close() None

关闭流并释放底层缓冲区。对已关闭的流进行进一步操作会引发 ValueError

getvalue() bytes

返回底层缓冲区当前的内容。

class io.BytesIO(alloc_size: int)

创建一个空的 BytesIO 对象,预先分配可容纳最多 alloc_size 个字节的空间,这样写入最多这么多字节时不会重新分配缓冲区(从而避免内存不足的情况或内存碎片化)。此构造函数是 MicroPython 的扩展,仅推荐用于特殊场景和系统级库,不适用于最终用户应用程序。

与 CPython 的差异

此构造函数是 MicroPython 的扩展。

read(size: int = -1) bytes

读取并返回最多 size 个字节。如果省略 size 或其为负值,则读取并返回所有剩余内容。

readline(size: int = -1) bytes

读取并返回一行。如果给定了 size,则最多读取 size 个字节。

readinto(buf: bytearray) int

读入预先分配的可写缓冲区 buf,并返回读取的字节数。

write(b: bytes) int

写入类字节对象 b 并返回写入的字节数。

seek(offset: int, whence: int = 0) int

将流位置更改为相对于 whenceoffset0 = 起始处,1 = 当前位置,2 = 末尾),并返回新的绝对位置。

tell() int

返回当前的流位置。

flush() None

刷新写缓冲区。对于内存中的流,此操作无任何效果。

close() None

关闭流并释放底层缓冲区。对已关闭的流进行进一步操作会引发 ValueError

getvalue() bytes

返回底层缓冲区当前的内容。