16.2. io — 处理流的核心工具


    模块提供了 Python 用于处理各种 I/O 类型的主要工具。三种主要的 I/O类型分别为: 文本 I/O, 二进制 I/O原始 I/O。这些是泛型类型,有很多种后端存储可以用在他们上面。一个隶属于任何这些类型的具体对象被称作 file object。 其他同类的术语还有 类文件对象

    独立于其类别,每个具体流对象也将具有各种功能:它可以是只读,只写或读写。它还可以允许任意随机访问(向前或向后寻找任何位置),或仅允许顺序访问(例如在套接字或管道的情况下)。

    All streams are careful about the type of data you give to them. For example giving a object to the method of a binary stream will raise a TypeError. So will giving a bytes object to the write() method of a text stream.

    在 3.3 版更改: 由于 现在是 OSError 的别名,因此用于引发 的操作现在会引发 OSError

    文本I/O预期并生成 对象。这意味着,无论何时后台存储是由字节组成的(例如在文件的情况下),数据的编码和解码都是透明的,并且可以选择转换特定于平台的换行符。

    创建文本流的最简单方法是使用 open(),可以选择指定编码:

    内存中文本流也可以作为 对象使用:

    1. f = io.StringIO("some initial text data")

    TextIOBase 的文档中详细描述了文本流的API

    16.2.1.2. 二进制 I/O

    二进制I/O(也称为缓冲I/O)预期 bytes-like objects 并产生 对象。不执行编码、解码或换行转换。这种类型的流可以用于所有类型的非文本数据,并且还可以在需要手动控制文本数据的处理时使用。

    创建二进制流的最简单方法是使用 open(),并在模式字符串中指定 'b'

    内存中二进制流也可以作为 对象使用:

    1. f = io.BytesIO(b"some initial binary data: \x00\x01")

    BufferedIOBase 的文档中详细描述了二进制流 API。

    其他库模块可以提供额外的方式来创建文本或二进制流。参见 的示例。

    16.2.1.3. 原始 I/O

    原始 I/O(也称为 非缓冲 I/O)通常用作二进制和文本流的低级构建块。用户代码直接操作原始流的用法非常罕见。不过,可以通过在禁用缓冲的情况下以二进制模式打开文件来创建原始流:

    The raw stream API is described in detail in the docs of .

    io.DEFAULT_BUFFER_SIZE

    包含模块缓冲 I/O 类使用的默认缓冲区大小的 int。 在可能的情况下 open() 将使用文件的 blksize(由 获得)。

    io.open(file, mode=’r’, buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

    这是内置的 open() 函数的别名。

    exception io.BlockingIOError

    这是内置的 异常的兼容性别名。

    exception io.UnsupportedOperation

    在流上调用不支持的操作时引发的继承 OSError 和 的异常。

    16.2.2.1. 内存中的流

    也可以使用 或 bytes-like object 作为文件进行读取和写入。对于字符串, 可以像在文本模式下打开的文件一样使用。 BytesIO 可以像以二进制模式打开的文件一样使用。两者都提供完整的随机读写功能。

    参见

    包含标准IO流: sys.stdin, 和 sys.stderr

    I/O 流的实现被组织为类的层次结构。 首先是 (ABC),用于指定流的各种类别,然后是提供标准流实现的具体类。

    I/O层次结构的顶部是抽象基类 IOBase 。它定义了流的基本接口。但是请注意,对流的读取和写入之间没有分隔。如果实现不支持指定的操作,则会引发 。

    RawIOBase ABC 是 的子类。它负责将字节读取和写入流中。 RawIOBase 的子类 提供计算机文件系统中文件的接口。

    BufferedIOBase ABC处理原始字节流 ( ) 上的缓冲。其子类 BufferedWriter、 和 BufferedRWPair 缓冲流是可读、可写以及可读写的。 为随机访问流提供缓冲接口。 BufferedIOBase 的另一个子类 是内存中字节流。

    TextIOBase ABC是 的另一个子类,它处理字节表示文本的流,并处理字符串之间的编码和解码。 TextIOWrapper 是其一个扩展,它是原始缓冲流( )的缓冲文本接口。最后, StringIO 是一个用于文本的内存流。

    参数名不是规范的一部分,只有 的参数才用作关键字参数。

    下表总结了抽象基类提供的 io 模块:

    class io.IOBase

    所有 I/O 类的抽象基类,作用于字节流。没有公共构造函数。

    此类为许多方法提供了空的抽象实现,派生类可以有选择地重写。默认实现无法读取、写入或查找的文件。

    Even though does not declare read(), readinto(), or write() because their signatures will vary, implementations and clients should consider those methods part of the interface. Also, implementations may raise a ValueError (or ) when operations they do not support are called.

    The basic type used for binary data read from or written to a file is bytes. Other are accepted as method arguments too. In some cases, such as readinto(), a writable object such as is required. Text I/O classes work with str data.

    请注意,在关闭的流上调用任何方法(甚至查询)都是未定义的(undefined)。在这种情况下,实现可能会引发 。

    IOBase (及其子类)支持迭代器协议,这意味着可以迭代 对象以产生流中的行。根据流是二进制流(产生字节)还是文本流(产生字符串),行的定义略有不同。请参见下面的 readline()

    is also a context manager and therefore supports the with statement. In this example, file is closed after the statement’s suite is finished—even if an exception occurs:

    1. with open('spam.txt', 'w') as file:
    2. file.write('Spam and eggs!')

    IOBase 提供以下数据属性和方法:

    • close()

      刷新并关闭此流。如果文件已经关闭,则此方法无效。文件关闭后,对文件的任何操作(例如读取或写入)都会引发 。

      为方便起见,允许多次调用此方法。但是,只有第一个调用才会生效。

    • closed

      如果流关闭,则为True。

    • fileno()

      返回流的基础文件描述符(整数)—如果存在。如果 IO 对象不使用文件描述符,则会引发 OSError

    • flush()

      刷新流的写入缓冲区(如果适用)。这对只读和非阻塞流不起作用。

    • isatty()

      如果流是交互式的(即连接到终端/tty设备),则返回 True

    • readable()

      如果可以读取流,则返回 True 。否则为 False ,且 read() 将引发 错误。

    • readline(size=-1)

      从流中读取并返回一行。 如果指定了 size,将至多读取 size 个字节。

      对于二进制文件行结束符总是 b'\n';对于文本文件,可以用将 newline 参数传给 open() 的方式来选择要识别的行结束符。

    • readlines(hint=-1)

      从流中读取并返回包含多行的列表。 可以指定 hint 来控制要读取的行数:如果(以字节/字符数表示的)所有行的总大小超出了 hint 则将不会读取更多的行。

      请注意使用 for line in file: ... 就足够对文件对象进行迭代了,可以不必调用 file.readlines()

    • seek(offset[, whence])

      将流位置修改到给定的字节 offsetoffset 将相对于由 whence 指定的位置进行解析。 whence 的默认值为 SEEK_SETwhence 的可用值有:

      • SEEK_SET0 – 流的开头(默认值);offset 应为零或正值

      • SEEK_CUR or 1 – 当前流位置;offset 可以为负值

      返回新的绝对位置。

      3.1 新版功能: SEEK_* 常量.

      3.3 新版功能: 某些操作系统还可支持其他的值,例如 os.SEEK_HOLEos.SEEK_DATA。 特定文件的可用值还会取决于它是以文本还是二进制模式打开。

    • seekable()

      如果流支持随机访问则返回 True。 如为 False,则 , tell() 和 将引发 OSError

    • 返回当前流的位置。

    • truncate(size=None)

      将流的大小调整为给定的 size 个字节(如果未指定 size 则调整至当前位置)。 当前的流位置不变。 这个调整操作可扩展或减小当前文件大小。 在扩展的情况下,新文件区域的内容取决于具体平台(在大多数系统上,额外的字节会填充为零)。 返回新的文件大小。

    在 3.5 版更改: 现在Windows在扩展时将文件填充为零。

    • writable()

      如果流支持写入则返回 True。 如为 False,则 write() 和 将引发 OSError

    • writelines(lines)

      将行列表写入到流。 不会添加行分隔符,因此通常所提供的每一行都带有末尾行分隔符。

    • __del__()

      为对象销毁进行准备。 提供了此方法的默认实现,该实现会调用实例的 close() 方法。

    class io.RawIOBase

    原始二进制 I/O 的基类。 它继承自 。 没有公共构造器。

    原始二进制 I/O 通常提供对下层 OS 设备或 API 的低层级访问,而不尝试将其封装到高层级的基元中(这是留给缓冲 I/O 和 Text I/O 的,将在下文中描述)。

    IOBase 的属性和方法之外, 还提供了下列方法:

    • read(size=-1)

      从对象中读取 size 个字节并将其返回。 作为一个便捷选项,如果 size 未指定或为 -1,则返回所有字节直到 EOF。 在其他情况下,仅会执行一次系统调用。 如果操作系统调用返回字节数少于 size 则此方法也可能返回少于 size 个字节。

      如果返回 0 个字节而 size 不为零 0,这表明到达文件末尾。 如果处于非阻塞模式并且没有更多字节可用,则返回 None

      默认实现会转至 readall() 和 。

    • readall()

      从流中读取并返回所有字节直到 EOF,如有必要将对流执行多次调用。

    • readinto(b)

      Read bytes into a pre-allocated, writable bytes-like object b, and return the number of bytes read. If the object is in non-blocking mode and no bytes are available, None is returned.

    • write(b)

      将给定的 b 写入到下层的原始流,并返回所写入的字节数。 这可以少于 b 的总字节数,具体取决于下层原始流的设定,特别是如果它处于非阻塞模式的话。 如果原始流设为非阻塞并且不能真正向其写入单个字节时则返回 None。 调用者可以在此方法返回后释放或改变 b,因此该实现应该仅在方法调用期间访问 b

    class io.BufferedIOBase

    支持某种缓冲的二进制流的基类。 它继承自 IOBase。 没有公共构造器。

    与 的主要差别在于 read(), 和 write() 等方法将(分别)尝试按照要求读取尽可能多的输入或是耗尽所有给定的输出,其代价是可能会执行一次以上的系统调用。

    除此之外,那些方法还可能引发 ,如果下层的原始数据流处于非阻塞模式并且无法接受或给出足够数据的话;不同于对应的 RawIOBase 方法,它们将永远不会返回 None

    并且, 方法也没有转向 readinto() 的默认实现。

    典型的 实现不应当继承自 RawIOBase 实现,而要包装一个该实现,正如 和 BufferedReader 所做的那样。

    IOBase 的现有成员以外还提供或重载了下列方法和属性:

    • raw

      由 处理的下层原始流 (RawIOBase 的实例)。 它不是 API 的组成部分并且不存在于某些实现中。

    • detach()

      从缓冲区分离出下层原始流并将其返回。

      在原始流被分离之后,缓冲区将处于不可用的状态。

      某些缓冲区例如 BytesIO 并无可从此方法返回的单独原始流的概念。 它们将会引发 。

      3.1 新版功能.

    • read(size=-1)

      读取并返回最多 size 个字节。 如果此参数被省略、为 None 或为负值,则读取并返回所有数据直到 EOF。 如果流已经到达 EOF 则返回一个空的 bytes 对象。

      如果此参数为正值,并且下层原始流不可交互,则可能发起多个原始读取以满足字节计数(直至先遇到 EOF)。 但对于可交互原始流,则将至多发起一个原始读取,并且简短的结果并不意味着已到达 EOF。

      会在下层原始流不处于阻塞模式,并且当前没有可用数据时被引发。

    • read1(size=-1)

      通过至多一次对下层流的 read() (或 ) 方法的调用读取并返回至多 size 个字节。 这适用于在 BufferedIOBase 对象之上实现你自己的缓冲区的情况。

    • readinto(b)

      Read bytes into a pre-allocated, writable b and return the number of bytes read.

      类似于 read(),可能对下层原始流发起多次读取,除非后者为交互式。

      会在下层原始流不处于阻塞模式,并且当前没有可用数据时被引发。

    • readinto1(b)

      将字节数据读入预先分配的可写 bytes-like object b,其中至多使用一次对下层原始流 (或 readinto()) 方法的调用。 返回所读取的字节数。

      会在下层原始流不处于阻塞模式,并且当前没有可用数据时被引发。

      3.5 新版功能.

    • write(b)

      写入给定的 bytes-like object b,并返回写入的字节数 (总是等于 b 的字节长度,因为如果写入失败则会引发 )。 根据具体实现的不同,这些字节可能被实际写入下层流,或是出于运行效率和冗余等考虑而暂存于缓冲区。

      当处于非阻塞模式时,如果需要将数据写入原始流但它无法在不阻塞的情况下接受所有数据则将引发 BlockingIOError

      调用者可能会在此方法返回后释放或改变 b,因此该实现应当仅在方法调用期间访问 b

    16.2.3.2. 原始文件 I/O

    class io.FileIO(name, mode=’r’, closefd=True, opener=None)

    FileIO 代表在 OS 层级上包含文件的字节数据。 它实现了 接口(因而也实现了 IOBase 接口)。

    name 可以是以下两项之一:

    • 代表将被打开的文件路径的字符串或 对象。 在此情况下 closefd 必须为 True (默认值) 否则将会引发异常。

    • 代表一个现有 OS 层级文件描述符的号码的整数,作为结果的 FileIO 对象将可访问该文件。 当 FileIO 对象被关闭时此 fd 也将被关闭,除非 closefd 设为 False

    mode 可以为 'r', 'w', 'x''a' 分别表示读取(默认模式)、写入、独占新建或添加。 如果以写入或添加模式打开的文件不存在将自动新建;当以写入模式打开时文件将先清空。 以新建模式打开时如果文件已存在则将引发 。 以新建模式打开文件也意味着要写入,因此该模式的行为与 'w' 类似。 在模式中附带 '+' 将允许同时读取和写入。

    该类的 read() (当附带正值参数调用时), readinto()write() 方法将只执行一次系统调用。

    可以通过传入一个可调用对象作为 opener 来使用自定义文件打开器。 然后通过调用 opener 并传入 (name, flags) 来获取文件对象所对应的下层文件描述符。 opener 必须返回一个打开文件描述符(传入 os.open 作为 opener 的结果在功能上将与传入 None 类似)。

    新创建的文件是 。

    有关 opener 参数的示例,请参见内置函数 open()

    在 3.3 版更改: 增加了 opener 参数。增加了 'x' 模式。

    在 3.4 版更改: 文件现在禁止继承。

    在 和 RawIOBase 的属性和方法之外, 还提供了下列数据属性:

    • mode

      构造函数中给定的模式。

    • name

      文件名。当构造函数中没有给定名称时,这是文件的文件描述符。

    16.2.3.3. 缓冲流

    相比原始 I/O,缓冲 I/O 流提供了针对 I/O 设备的更高层级接口.

    class io.BytesIO([initial_bytes])

    一个使用内存字节缓冲区的流实现。 它继承自 。 在 close() 方法被调用时将会丢弃缓冲区。

    可选参数 initial_bytes 是一个包含初始数据的 。

    BytesIO 在继承自 和 IOBase 的成员以外还提供或重载了下列方法:

    • getbuffer()

      返回一个对应于缓冲区内容的可读写视图而不必拷贝其数据。 此外,改变视图将透明地更新缓冲区内容:

      注解

      只要视图保持存在, 对象就无法被改变大小或关闭。

      3.2 新版功能.

    • getvalue()

      返回包含整个缓冲区内容的 bytes

    • read1()

      In , this is the same as read().

    • readinto1()

      In BytesIO, this is the same as readinto().

      3.5 新版功能.

    class io.BufferedReader(raw, buffer_size=DEFAULT_BUFFER_SIZE)

    一个提供对可读的序列型 对象更高层级访问的缓冲区。 它继承自 BufferedIOBase。 当从此对象读取数据时,可能会从下层原始流请求更大量的数据,并存放到内部缓冲区中。 接下来可以在后续读取时直接返回缓冲数据。

    根据给定的可读 raw 流和 buffer_size 创建 的构造器。 如果省略 buffer_size,则会使用 DEFAULT_BUFFER_SIZE

    • peek([size])

      从流返回字节数据而不前移位置。 完成此调用将至多读取一次原始流。 返回的字节数量可能少于或多于请求的数量。

    • read([size])

      读取并返回 size 个字节,如果 size 未给定或为负值,则读取至 EOF 或是在非阻塞模式下读取调用将会阻塞。

    • read1(size)

      在原始流上通过单次调用读取并返回至多 size 个字节。 如果至少缓冲了一个字节,则只返回缓冲的字节。 在其他情况下,将执行一次原始流读取。

    class io.BufferedWriter(raw, buffer_size=DEFAULT_BUFFER_SIZE)

    一个提供对可读的序列型 对象更高层级访问的缓冲区。 它继承自 BufferedIOBase。 当写入到此对象时,数据通常会被放入到内部缓冲区中。 缓冲区将在满足某些条件的情况下被写到下层的 对象,包括:

    • 当缓冲区对于所有挂起数据而言太小时;

    • flush() 被调用时

    • 当(为 对象)请求 seek() 时;

    该构造器会为给定的可写 raw 流创建一个 BufferedWriter。 如果未给定 buffer_size,则使用默认的 。

    BufferedWriter 在继承自 和 IOBase 的成员以外还提供或重载了下列方法:

    • flush()

      将缓冲区中保存的字节数据强制放入原始流。 如果原始流发生阻塞则应当引发 。

    • write(b)

      写入 bytes-like object b 并返回写入的字节数。 当处于非阻塞模式时,如果缓冲区需要被写入但原始流发生阻塞则将引发 。

    class io.BufferedRandom(raw, buffer_size=DEFAULT_BUFFER_SIZE)

    随机访问流的带缓冲的接口。 它继承自 BufferedReader 和 ,并进一步支持 seek()tell() 功能。

    该构造器会为在第一个参数中给定的可查找原始流创建一个读取器和写入器。 如果省略 buffer_size 则使用默认的 DEFAULT_BUFFER_SIZE

    能做到 BufferedReader 或 所能做的任何事。

    class io.BufferedRWPair(reader, writer, buffer_size=DEFAULT_BUFFER_SIZE)

    一个带缓冲的 I/O 对象,它将两个单向 RawIOBase 对象 – 一个可读,另一个可写 – 组合为单个双向端点。 它继承自 。

    readerwriter 分别是可读和可写的 RawIOBase 对象。 如果省略 buffer_size 则使用默认的 。

    BufferedRWPair 实现了 的所有方法,但 detach() 除外,调用该方法将引发 。

    警告

    BufferedRWPair 不会尝试同步访问其下层的原始流。 你不应当将传给它与读取器和写入器相同的对象;而要改用 。

    16.2.3.4. 文本 I/O

    class io.TextIOBase

    Base class for text streams. This class provides a character and line based interface to stream I/O. There is no readinto() method because Python’s character strings are immutable. It inherits . There is no public constructor.

    TextIOBase 在来自 的成员以外还提供或重载了以下数据属性和方法:

    • encoding

      用于将流的字节串解码为字符串以及将字符串编码为字节串的编码格式名称。

    • errors

      解码器或编码器的错误设置。

    • 一个字符串、字符串元组或者 None,表示目前已经转写的新行。 根据具体实现和初始构造器旗标的不同,此属性或许会不可用。

    • buffer

      TextIOBase 处理的下层二进制缓冲区(为一个 的实例)。 它不是 TextIOBase API 的组成部分并且不存在于某些实现中。

    • detach()

      从 分离出下层二进制缓冲区并将其返回。

      在下层缓冲区被分离后,TextIOBase 将处于不可用的状态。

      某些 的实现,例如 StringIO 可能并无下层缓冲区的概念,因此调用此方法将引发 。

      3.1 新版功能.

    • read(size=-1)

      从流中读取至多 size 个字符并以单个 str 的形式返回。 如果 size 为负值或 None,则读取至 EOF。

    • readline(size=-1)

      读取至换行符或 EOF 并返回单个 str。 如果流已经到达 EOF,则将返回一个空字符串。

      如果指定了 size ,最多将读取 size 个字符。

    • seek(offset[, whence])

      将流位置改为给定的偏移位置 offset。 具体行为取决于 whence 形参。 whence 的默认值为 SEEK_SET

      • SEEK_SET0: 从流的开始位置起查找(默认值);offset 必须为 所返回的数值或为零。 任何其他 offset 值都将导致未定义的行为。

      • SEEK_CUR1: “查找” 到当前位置;offset 必须为零,表示无操作(所有其他值均不受支持)。

      • SEEK_END2: 查找到流的末尾;offset 必须为零(所有其他值均不受支持)。

      以数字形式返回新的绝对位置。

      3.1 新版功能: SEEK_* 常量.

    • tell()

      以不透明数字形式返回当前流的位置。 该数字通常并不代表下层二进制存储中对应的字节数。

    • write(s)

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

    class io.TextIOWrapper(buffer, encoding=None, errors=None, newline=None, line_buffering=False, write_through=False)

    一个基于 BufferedIOBase 二进制流的缓冲文本流。 它继承自 。

    encoding 给出流被解码或编码时将使用的编码格式。 它默认为 locale.getpreferredencoding(False)

    errors 是一个可选的字符串,它指明编码格式和编码格式错误的处理方式。 传入 'strict' 将在出现编码格式错误时引发 (默认值 None 具有相同的效果),传入 'ignore' 将忽略错误。 (请注意忽略编码格式错误会导致数据丢失。) 'replace' 会在出现错误数据时插入一个替换标记 (例如 '?')。 'backslashreplace' 将把错误数据替换为一个反斜杠转义序列。 在写入时,还可以使用 'xmlcharrefreplace' (替换为适当的 XML 字符引用) 或 'namereplace' (替换为 \N{...} 转义序列)。 任何其他通过 codecs.register_error() 注册的错误处理方式名称也可以被接受。

    newline 控制行结束符处理方式。 它可以为 None, '', '\n', '\r''\r\n'。 其工作原理如下:

    • 当从流中读取输入时,如果 newlineNone,则会启用 模式。 输入中的行结束符可以为 '\n', '\r''\r\n',在返回给调用者之前它们会被统一转写为 '\n'。 如果参数为 '',也会启用通用换行模式,但行结束符会不加转写即返回给调用者。 如果它具有任何其他合法的值,则输入行将仅由给定的字符串结束,并且行结束符会不加转写即返回给调用者。

    • 将输出写入流时,如果 newlineNone,则写入的任何 '\n' 字符都将转换为系统默认行分隔符 os.linesep。如果 newline'''\n',则不进行翻译。如果 newline 是任何其他合法值,则写入的任何 '\n' 字符将被转换为给定的字符串。

    如果 line_bufferingTrue,则当一个写入调用包含换行符或回车时将会应用 flush()

    如果 write_throughTrue,对 write() 的调用会确保不被缓冲:在 对象上写入的任何数据会立即交给其下层的 buffer 来处理。

    在 3.3 版更改: 已添加 write_through 参数

    在 3.3 版更改: 默认的 encoding 现在将为 locale.getpreferredencoding(False) 而非 locale.getpreferredencoding()。 不要使用 locale.setlocale() 来临时改变区域编码格式,要使用当前区域编码格式而不是用户的首选编码格式。

    provides one attribute in addition to those of TextIOBase and its parents:

    • line_buffering

      是否启用行缓冲。

    class io.StringIO(initial_value=’’, newline=’\n’)

    用于文本 I/O 的内存数据流。 当调用 方法时将会丢弃文本缓冲区。

    缓冲区的初始值可通过提供 initial_value 来设置。 如果启用了行结束符转写,换行将以 write() 所用的方式被编码。 数据流位置将被设为缓冲区的开头。

    newline 参数的规则与 所用的一致。 默认规则是仅将 \n 字符视为行结束符并且不执行换行符转写。 如果 newline 设为 None,在所有平台上换行符都将被写入为 \n,但当读取时仍然会执行通用换行编码格式。

    StringIO 在继承自 及其父类的现有成员以外还提供了以下方法:

    • getvalue()

      返回一个包含缓冲区全部内容的 str。 换行符会以与 read() 相同的方式被编码,但是流的位置不会被改变。

    用法示例:

    1. import io
    2. output = io.StringIO()
    3. output.write('First line.\n')
    4. print('Second line.', file=output)
    5. # Retrieve file contents -- this will be
    6. # 'First line.\nSecond line.\n'
    7. contents = output.getvalue()
    8. # Close object and discard memory buffer --
    9. # .getvalue() will now raise an exception.

    class io.IncrementalNewlineDecoder

    用于在 模式下解码换行符的辅助编解码器。 它继承自 codecs.IncrementalDecoder

    本节讨论所提供的具体 I/O 实现的性能。

    即使在用户请求单个字节时,也只读取和写入大块数据。通过该方法,缓冲 I/O 也隐藏了调用和执行操作系统无缓冲 I/O 例程时的任何低效性。。增益取决于操作系统和执行的 I/O 类型。例如,在某些现代操作系统上(例如Linux),无缓冲磁盘 I/O 可以与缓冲 I/O 一样快。但最重要的是,无论平台和支持设备如何,缓冲 I/O 都能提供可预测的性能。因此,对于二进制数据,几乎总是首选使用缓冲的I / O而不是未缓冲的I / O。

    16.2.4.2. 文本 I/O

    二进制存储(如文件)上的文本 I/O 比同一存储上的二进制 I/O 慢得多,因为它需要使用字符编解码器在Unicode和二进制数据之间进行转换。这在处理大量文本数据(如大型日志文件)时会变得非常明显。此外,由于使用的重构算法 TextIOWrapper.tell() 和 都相当慢。

    StringIO 是原生的内存 Unicode 容器,速度与 相似。

    16.2.4.3. 多线程

    对象是线程安全的,只要它们包装的操作系统调用(比如Unix下的 read(2) )也是线程安全的。

    二进制缓冲对象(例如 BufferedReader, , BufferedRandom 和 )使用锁来保护其内部结构;因此,可以安全地一次从多个线程中调用它们。

    TextIOWrapper 对象不再是线程安全的。

    16.2.4.4. 可重入性

    二进制缓冲对象( BufferedReader , , BufferedRandom 和 的实例)不是可重入的。虽然在正常情况下不会发生可重入调用,但是可能会在 signal 处理程序中执行 I/O 而产生。如果线程尝试重新输入已经访问的缓冲对象,则会引发 。注意,这并不禁止其他线程进入缓冲对象。

    上面的内容隐含地扩展到文本文件,因为 open() 函数会把缓冲对象包装在 中。这包括标准流,因此也会影响内置函数 print()