Python 2中BytesIO的getbuffer等价方法是什么?

4
在Python 3中,我可以通过object.getbuffer().nbytes(其中object = ByteIO())获取ByteIO对象的大小,但在Python 2中,什么是等价于getbuffer()最佳方法?经过一些探索,我发现我可以使用len(object.getvalue())sys.getsizeof(object),但我不知道Python 2是否会接受它们。

请注意,这不是 BytesIO 对象的大小,而是底层缓冲区的字节数。但是,为什么你不直接尝试在 Python 2 中是否可以使用 len(object.getvalue()) 呢? - juanpa.arrivillaga
它确实可以,但我不确定它是否能够可靠地输出与“getbuffer().nbytes”相同的结果。 - Brian Lee
它将适用于 io.BytesIO 对象。 - juanpa.arrivillaga
此外,sys.getsizeof(object)不等同于 - juanpa.arrivillaga
3个回答

7

请查看下面的重要更新

在研究Python 2.7源代码后,我找到了一个简单的解决方案:因为io.BytesIO()返回文件描述符,它具有一组标准函数,包括tell()

请注意,间接方法(例如len(fd.getvalue())fd.getbuffer().nbytes)会将缓冲区复制出来,然后计算缓冲区大小。在我的情况下,当缓冲区占用内存的一半时,这最终导致应用程序崩溃 :/

相反,fd.tell()仅报告描述符的当前位置,不需要任何内存分配!

请注意,sys.getsizeof(fd)fd.__sizeof__()不会返回正确的缓冲区大小。

>>> from io  import BytesIO
>>> from sys import getsizeof
>>> with BytesIO() as fd:              
...  for x in xrange(200):
...   fd.write(" ")
...   print fd.tell(), fd.__sizeof__(), getsizeof(fd)
1 66 98
2 66 98
3 68 100
4 68 100
5 70 102
6 70 102
.....
194 265 297
195 265 297
196 265 297
197 265 297
198 265 297
199 265 297
200 265 297

更新

在 @admaster 和 @Artemis 的评论后,我意识到,在预设缓冲区的情况下,正确的方法是将指针移动到缓冲区的末尾。标准的 seek 函数可以做到这一点,并且它会报告当前缓冲区的大小。

buffsize = fd.seek(0,2)

所以这里是如何在不必要地复制内存的情况下完成它

from io import BytesIO
x = BytesIO(b'AAAAAA')
x.tell()    # returns 0
x.seek(0,2) # returns 6


# However
x = BytesIO()
x.write(b'AAAAAA')
x.tell()    # returns 6
x.seek(0,2) # returns 6

如果Byteio对象是从另一个缓冲区初始化的,则tell()将返回0(例如,BytesIO(b'00').tell()),因此getvalue()更可靠。 - eadmaster
@eadmaster,你应该报告这个bug!tell() 必须报告已写入数据的当前大小,并且如果数据是从另一个缓冲区借用的,则必须报告该缓冲区中数据的大小。 - rth
正如另一个答案所提到的,tell()函数并没有报告“已写入数据的当前大小”,它报告的是指针的当前位置。只有当指针当前位于文件末尾时,它才会是当前数据的大小。 - Artemis

2

1
值得注意的是,tell()只会返回文件描述符的当前位置,而不一定是缓冲区的大小。
可以从以下示例中看到这一点:
from io import BytesIO
x = BytesIO(b'AAAAAA')
x.tell() # returns 0
x.read()
x.tell() # Now it returns 6

# However
x = BytesIO()
x.write(b'AAAAAA')
x.tell() # returns 6

在第一个示例中,我们使用字节字符串初始化了对象,但文件描述符仍然在开头,因此返回0,然后我们读取流,这意味着我们的文件描述符将移动到末尾,因此返回6。
在第二个示例中,我们初始化一个空的BytesIO对象,并将字节字符串写入其中,因此文件描述符现在位于流的末尾,因此我们不需要读取它来更新描述符。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接