下面的示例脚本将一些字符串写到一个文件中,使用了 "w"
,文本,或 "wb"
,二进制模式。
import itertools as it
from string import ascii_lowercase
import time
characters = it.cycle(ascii_lowercase)
mode = 'w'
# mode = 'wb' # using this mode takes longer to execute
t1 = time.clock()
with open('test.txt', mode) as fh:
for __ in xrange(10**7):
fh.write(''.join(it.islice(characters, 0, 50)))
t2 = time.clock()
print 'Mode: {}, time elapsed: {:.2f}'.format(mode, t2 - t1)
在Python 2中,使用 "w"
模式下执行,我发现它在 24.89 +/- 0.02 s
而使用 "wb"
需要 25.67 +/- 0.02 s
来执行。这些是每个模式连续运行三次的具体时间。
mode_w = [24.91, 24.86, 24.91]
mode_wb = [25.68, 25.64, 25.69]
我对这些结果感到惊讶,因为Python 2无论如何都是把它的字符串作为二进制字符串来存储的,所以既没有 "w"
也不 "wb"
需要执行任何编码工作。而文本模式则需要执行额外的工作,如 检查行尾:
默认情况下,使用文本模式,这可能会转换为
'\n'
写入时将字符转换为特定平台的表示,读取时再转换为特定平台的表示。
所以,如果说我希望文本模式 "w"
比二进制模式耗时更长 "wb"
. 然而,情况似乎恰恰相反。为什么会这样?
用CPython 2.7.12测试。
从源码上看 file.write
揭示了以下的区别 二进制模式 和 文本模式:
if (f->f_binary) {
if (!PyArg_ParseTuple(args, "s*", &pbuf))
return NULL;
s = pbuf.buf;
n = pbuf.len;
}
else {
PyObject *text;
if (!PyArg_ParseTuple(args, "O", &text))
return NULL;
if (PyString_Check(text)) {
s = PyString_AS_STRING(text);
n = PyString_GET_SIZE(text);
}
这里 f->f_binary
的模式时,设置为 open
包括 "b"
. 在这种情况下,Python从字符串对象中构造一个辅助缓冲区对象,然后获取数据。s
和长度 n
的缓冲区。我想这是为了与其他支持缓冲区接口的对象兼容(通用性)。
这里 PyArg_ParseTuple(args, "s*", &pbuf)
创建相应的 缓冲对象. 这个操作需要额外的计算时间,而在文本模式下工作时,Python只是简单地解析参数 作为一个对象("O"
) 几乎没有成本。检索数据和长度通过
s = PyString_AS_STRING(text);
n = PyString_GET_SIZE(text);
同时进行 缓冲区创建时.
这意味着在二进制模式下工作时,从字符串对象中创建一个辅助缓冲区对象会带来额外的开销。因此,在二进制模式下工作时,执行时间会更长。