эквивалент getbuffer для BytesIO в Python 2

В Python 3 я могу получить размер объекта ByteIO через object.getbuffer().nbytes (где object = ByteIO()), но что будет лучшим эквивалентом для getbuffer() в Python 2? Проведя некоторые исследования, я обнаружил, что могу использовать len(object.getvalue()) или sys.getsizeof(object), но я не знаю, примет ли их Python 2.


person Brian Lee    schedule 14.08.2017    source источник
comment
Обратите внимание, что это не размер объекта BytesIO, это количество байтов базового буфера. Но почему вы просто не пробовали, работает ли len(object.getvalue()) в Python 2 или нет?   -  person juanpa.arrivillaga    schedule 14.08.2017
comment
Это так, но я не уверен, что он будет надежно выводить тот же результат, что и getbuffer().nbytes   -  person Brian Lee    schedule 14.08.2017
comment
Это будет для io.BytesIO объектов.   -  person juanpa.arrivillaga    schedule 14.08.2017
comment
Кроме того, sys.getsizeof(object) не будет не эквивалентным.   -  person juanpa.arrivillaga    schedule 14.08.2017


Ответы (3)


см. критическое обновление ниже

Покопавшись в исходном коде Python 2.7, я нашел простое решение: поскольку io.BytesIO() возвращает дескриптор файла, он имеет стандартный набор функций, включая tell().

Обратите внимание, что косвенные методы, такие как len(fd.getvalue()) или fd.getbuffer().nbytes, копируют буфер, а затем вычисляют размер буфера. В моем случае, когда буфер занимает 1/2 памяти, это заканчивается крахом приложения :/

Напротив, 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.seek(0,2) # returns 6
x.tell()    # returns 6

# However
x = BytesIO()
x.write(b'AAAAAA')
x.seek(0,2) # returns 6
x.tell()    # returns 6
person rth    schedule 03.01.2019
comment
если объект Byteio инициализируется из другого буфера, tell() вернет 0 (например, BytesIO(b'00').tell()), поэтому getvalue() более надежен. - person eadmaster; 29.01.2020
comment
@eadmaster, вы должны сообщить об этой ошибке! tell() должен сообщать о текущем размере записанных данных, а если данные были заимствованы из другого буфера, он должен сообщать о размере данных в этом буфере. - person rth; 29.01.2020
comment
@rth, как отмечает другой ответ, tell() не сообщает о текущем размере записанных данных, а сообщает о текущей позиции указателя. Это будет только текущий размер данных, если указатель в данный момент находится в конце файла. - person Artemis; 19.02.2021
comment
@Artemis согласен, я обновил свой ответ. Спасибо. - person rth; 20.02.2021

Вы можете использовать getvalue()

Пример:

from io import BytesIO
if __name__ == "__main__":
    out = BytesIO()
    out.write(b"test\0")
    print len(out.getvalue())

См.: https://docs.python.org/2/library/io.html#io.BytesIO.getvalue

person Ale    schedule 14.11.2017

Стоит отметить, что 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 и записываем в него нашу строку байтов, в результате дескриптор файла теперь находится в конце потока, и поэтому нам не нужно его читать для обновления дескриптора.

person NickS2089    schedule 30.03.2020