Python 3.x Python3开放缓冲参数看起来很奇怪
从 缓冲是用于设置缓冲策略的可选整数。 传递0以关闭缓冲(仅在二进制模式下允许),传递1至 选择行缓冲(仅在文本模式下可用),然后选择一个大于1的整数 以字节为单位指示固定大小区块缓冲区的大小。当没有 如果给定缓冲参数,则默认缓冲策略的作用如下 如下: 二进制文件缓冲在固定大小的块中;缓冲区的大小是使用一种启发式方法来选择的,该方法试图确定底层缓冲区的大小 设备的“块大小”和返回io.DEFAULT\U BUFFER\U大小。在…上 在许多系统中,缓冲区通常为4096或8192字节长。 “交互式”文本文件(isatty()返回True的文件)使用行缓冲。其他文本文件使用上述策略 对于二进制文件 我用文本模式打开一个名为Python 3.x Python3开放缓冲参数看起来很奇怪,python-3.x,io,python-3.7,Python 3.x,Io,Python 3.7,从 缓冲是用于设置缓冲策略的可选整数。 传递0以关闭缓冲(仅在二进制模式下允许),传递1至 选择行缓冲(仅在文本模式下可用),然后选择一个大于1的整数 以字节为单位指示固定大小区块缓冲区的大小。当没有 如果给定缓冲参数,则默认缓冲策略的作用如下 如下: 二进制文件缓冲在固定大小的块中;缓冲区的大小是使用一种启发式方法来选择的,该方法试图确定底层缓冲区的大小 设备的“块大小”和返回io.DEFAULT\U BUFFER\U大小。在…上 在许多系统中,缓冲区通常为4096或8192字节长。 “交互式
test.log
的文件,并将缓冲设置为16。所以我认为区块大小是16,当我将32字节的字符串写入文件时。它将调用write
(syscall)两次。但实际上,它只调用一次
使用strace-e write python3 test.py
跟踪系统调用,并获得以下信息
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 320) = 320
缓冲是什么意思?这个答案适用于CPython 3.7,其他Python实现可能有所不同 文本模式下的
open()
函数返回。\u io.TextIOWrapper()
调用了大小为8192字节的内部“buffer”(它是),对于文本模式w
它还具有\u io.BufferedWriter()
的句柄,对于文本模式a
它具有\u io.BufferedRandom()
的句柄。\u io.BufferedWriter()
/\u io.BufferedRandom()
的大小由open()
函数中的参数buffering
指定
当您调用到\u io.TextIOWrapper().write(“一些文本”)
时,它会将文本添加到内部挂起的\u字节
缓冲区中。在一些写入之后,您将填充挂起的\u字节
缓冲区,然后它将被写入\u io.BufferedWriter()
内的缓冲区。当您同时填充\u io.BufferedWriter()
中的缓冲区时,它将被写入目标文件
当您以二进制模式打开文件时,您将直接从缓冲
参数中获得使用缓冲区大小初始化的io.BufferedWriter()
/\io.BufferedRandom()
对象
让我们看一些例子。我将从使用二进制模式的更简单的一个开始
# Case 1
with open('test.log', 'wb', buffering=16) as f:
for _ in range(5):
f.write(b'a'*15)
strace输出:
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 8192) = 8192
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 24576) = 24576
在第一次迭代中,它用15个字节填充缓冲区。在第二次迭代中,它发现再添加15个字节会使缓冲区溢出,因此它首先刷新缓冲区(调用systemwrite
),然后保存这些新的15个字节。在下一次迭代中,同样的情况再次发生。在缓冲区中的最后一次迭代之后是15b,在文件结束时写入(将保留在
上下文中)
第二种情况,我将尝试向缓冲区写入比缓冲区大小更多的数据:
# Case 2
with open('test.log', 'wb', buffering=16) as f:
for _ in range(5):
f.write(b'a'*17)
strace输出:
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 8192) = 8192
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 24576) = 24576
这里发生的情况是,在第一次迭代中,它将尝试写入缓冲区17b,但它无法适应缓冲区17b,因此直接写入文件,缓冲区保持为空。这适用于每个迭代
现在让我们看看文本模式
# Case 3
with open('test.log', 'w', buffering=16) as f:
for _ in range(5):
f.write('a'*8192)
strace输出:
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 8192) = 8192
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 24576) = 24576
首先回想一下,pending_bytes
的大小为8192b。在第一次迭代中,它将8192字节(从代码:'a'*8192
)写入pending_bytes
缓冲区。在第二次迭代中,它将另外8192个字节添加到pending_buffer
中,发现它大于8192(大小为pending_buffer
buffer),并将其写入底层\u io.BufferedWriter()
。\u io.BufferedWriter()
中的缓冲区大小为16 B(缓冲
参数),因此它将立即写入文件(与案例2相同)。现在,pending_buffer
是空的,在第三次迭代中,它再次被8192b填充。在第四次迭代中,它添加了另一个8192bpending_字节
buffer溢出,并再次像在第二次迭代中一样直接写入文件。在上一次迭代中,它将8192b添加到挂起字节
缓冲区中,当文件关闭时,缓冲区将被刷新
最后一个示例包含大于8192B的缓冲。为了更好地解释,我又添加了2次迭代
# Case 4
with open('test.log', 'w', buffering=30000) as f:
for _ in range(7):
f.write('a'*8192)
strace输出:
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 8192) = 8192
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 24576) = 24576
迭代:
挂起的\u字节中
pending_字节
中,但它超过了最大大小,因此它被写入基础\u io.BufferedWriter()
中并保持在那里(pending_字节
现在为空)挂起的\u字节中
挂起的\u字节中
,但它超过了最大大小,因此它尝试写入基础io.BufferedWriter()
。但它将超过底层缓冲区的最大容量,原因是16384+16384>30000
(从迭代2开始,第一个16384b仍然存在),因此它首先将旧的16384b写入文件,然后将那些新的16384b(从挂起的字节
)放入缓冲区。(现在,pending_字节
缓冲区再次为空)pending_buffer
是空的,\u io.bufferedWriter()
包含16384 B。在这个迭代中,它用8192 B填充pending_buffer
。就是这样时,它关闭文件。关闭过程如下:
将8192 B从挂起缓冲区写入io.BufferedWriter()
(可能原因8192+16384<30000
)
将(8192+16384
=)24576 B写入文件
关闭文件描述符
顺便说一句,目前我不知道为什么会有