我正在学习如何在Python中使用流,我注意到IO docs说了以下内容:
创建二进制流的最简单方法是在模式字符串中使用open()和'b':
f = open("myfile.jpg", "rb")
内存中的二进制流也可用作BytesIO对象:
f = io.BytesIO(b"some initial binary data: \x00\x01")
f
定义的open
和f
定义的BytesIO
之间有何区别?换句话说,什么是“内存中的二进制流”,它与open
的不同之处是什么?
答案 0 :(得分:51)
为简单起见,我们暂时考虑写作而不是阅读。
所以,当您使用open()
时,请说:
with open("test.dat", "wb") as f:
f.write(b"Hello World")
f.write(b"Hello World")
f.write(b"Hello World")
执行后,将创建一个名为test.dat
的文件,其中包含Hello World
。在将数据写入文件后,数据不会保存在内存中(除非由名称保存)。
现在,当您考虑io.BytesIO()
时:
with io.BytesIO() as f:
f.write(b"Hello World")
f.write(b"Hello World")
f.write(b"Hello World")
不是将内容写入文件,而是将其写入内存缓冲区。换句话说,一块RAM。基本上写下以下内容是等效的:
buffer = b""
buffer += b"Hello World"
buffer += b"Hello World"
buffer += b"Hello World"
关于带有with语句的示例,最后还会有del buffer
。
这里的关键区别是优化和性能。 io.BytesIO
能够进行一些优化,使其比简单地逐个连接所有b"Hello World"
更快。
只是为了证明它是一个小基准:
import io
import time
begin = time.time()
buffer = b""
for i in range(0, 50000):
buffer += b"Hello World"
end = time.time()
seconds = end - begin
print("Concat:", seconds)
begin = time.time()
buffer = io.BytesIO()
for i in range(0, 50000):
buffer.write(b"Hello World")
end = time.time()
seconds = end - begin
print("BytesIO:", seconds)
除了性能提升之外,使用BytesIO
而不是连接具有可以使用BytesIO
代替文件对象的优势。所以说你有一个期望文件对象写入的函数。然后你可以给它内存缓冲区而不是文件。
不同之处在于open("myfile.jpg", "rb")
只是加载并返回myfile.jpg
的内容;而BytesIO
再次只是一个包含一些数据的缓冲区。
由于BytesIO
只是一个缓冲区 - 如果您想稍后将内容写入文件 - 您必须这样做:
buffer = io.BytesIO()
# ...
with open("test.dat", "wb") as f:
f.write(buffer.getvalue())
另外,你没有提到一个版本;我使用的是Python 3.与示例相关:我使用with语句而不是调用f.close()
答案 1 :(得分:6)
使用open
打开硬盘上的文件。根据您使用的模式,您可以从磁盘读取或写入(或两者)。
BytesIO
对象与磁盘上的任何实际文件无关。它只是一块内存,就像文件一样。它与从open
返回的文件对象具有相同的API(模式r+b
,允许读取和写入二进制数据)。
BytesIO
(并且它总是处于文本模式时,它是紧密的兄弟StringIO
)当您需要将数据传入或传出期望获得文件对象的API时,它会非常有用,但是你更喜欢直接传递数据。在将输入数据提供给库之前,您可以将其输入BytesIO
。返回后,您可以使用BytesIO
方法从getvalue()
获取库写入文件的任何数据。 (当然,你通常只需要做其中一个。)
答案 2 :(得分:0)
f = open("myfile.jpg", "rb")
从磁盘磁盘中读取文件中的字节,并将此值赋给引用为'f'的对象,该对象由Python保存在内存中。
f = io.BytesIO(b"some initial binary data: \x00\x01")
将字节流值分配给引用为'f'的对象,该对象由Python保存在内存中。