Подтвердить что ты не робот

Как очистить объект stringio?

У меня есть созданный объект stringio, и в нем есть текст. Я хотел бы очистить его существующие ценности и повторно использовать его, а не вспоминать об этом. Есть ли способ сделать это?

4b9b3361

Ответ 1

TL; DR

Не утруждайте себя очисткой, просто создайте новую - быстрее.

Метод

Python 2

Вот как бы я нашел такие вещи:

>>> from StringIO import StringIO
>>> dir(StringIO)
['__doc__', '__init__', '__iter__', '__module__', 'close', 'flush', 'getvalue', 'isatty', 'next', 'read', 'readline', 'readlines', 'seek', 'tell', 'truncate', 'write', 'writelines']
>>> help(StringIO.truncate)
Help on method truncate in module StringIO:

truncate(self, size=None) unbound StringIO.StringIO method
    Truncate the file size.

    If the optional size argument is present, the file is truncated to
    (at most) that size. The size defaults to the current position.
    The current file position is not changed unless the position
    is beyond the new file size.

    If the specified size exceeds the file current size, the
    file remains unchanged.

Итак, вы хотите .truncate(0). Но, вероятно, дешевле (и проще) инициализировать новый StringIO. См. Ниже критерии.

Python 3

(Благодаря tstone2077 для указав разницу.)

>>> from io import StringIO
>>> dir(StringIO)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'getvalue', 'isatty', 'line_buffering', 'newlines', 'read', 'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines']
>>> help(StringIO.truncate)
Help on method_descriptor:

truncate(...)
    Truncate size to pos.

    The pos argument defaults to the current file position, as
    returned by tell().  The current file position is unchanged.
    Returns the new absolute position.

Важно отметить, что теперь текущая позиция файла не изменилась, тогда как усечение до нуля было бы reset позицией в варианте Python 2.

Таким образом, для Python 2 вам нужно только

>>> from cStringIO import StringIO
>>> s = StringIO()
>>> s.write('foo')
>>> s.getvalue()
'foo'
>>> s.truncate(0)
>>> s.getvalue()
''
>>> s.write('bar')
>>> s.getvalue()
'bar'

Если вы сделаете это в Python 3, вы не получите ожидаемого результата:

>>> from io import StringIO
>>> s = StringIO()
>>> s.write('foo')
3
>>> s.getvalue()
'foo'
>>> s.truncate(0)
0
>>> s.getvalue()
''
>>> s.write('bar')
3
>>> s.getvalue()
'\x00\x00\x00bar'

Итак, в Python 3 вам также нужно reset позицию:

>>> from cStringIO import StringIO
>>> s = StringIO()
>>> s.write('foo')
3
>>> s.getvalue()
'foo'
>>> s.truncate(0)
0
>>> s.seek(0)
0
>>> s.getvalue()
''
>>> s.write('bar')
3
>>> s.getvalue()
'bar'

Если использовать метод truncate в коде Python 2, безопаснее одновременно вызывать seek(0) (до или после, это не имеет значения), чтобы код не прерывался, когда вы его неизбежно портировали на Python 3. И еще одна причина, почему вы должны просто создать новый объект StringIO!

Времена

Python 2

>>> from timeit import timeit
>>> def truncate(sio):
...     sio.truncate(0)
...     return sio
... 
>>> def new(sio):
...     return StringIO()
... 

Когда пуст, с StringIO:

>>> from StringIO import StringIO
>>> timeit(lambda: truncate(StringIO()))
3.5194039344787598
>>> timeit(lambda: new(StringIO()))
3.6533868312835693

С 3 Кбайт данных в, с StringIO:

>>> timeit(lambda: truncate(StringIO('abc' * 1000)))
4.3437709808349609
>>> timeit(lambda: new(StringIO('abc' * 1000)))
4.7179079055786133

И то же самое с cStringIO:

>>> from cStringIO import StringIO
>>> timeit(lambda: truncate(StringIO()))
0.55461597442626953
>>> timeit(lambda: new(StringIO()))
0.51241087913513184
>>> timeit(lambda: truncate(StringIO('abc' * 1000)))
1.0958449840545654
>>> timeit(lambda: new(StringIO('abc' * 1000)))
0.98760509490966797

Итак, игнорируя проблемы с потенциальной памятью (del oldstringio), быстрее обрезается StringIO.StringIO (на 3% быстрее для пустого, на 8% быстрее для 3 Кбайт данных), но быстрее ( "быстрее" ) для создания новый cStringIO.StringIO (на 8% быстрее для пустых, на 10% быстрее для 3 Кбайт данных). Поэтому я бы посоветовал просто использовать самый простой из них, поэтому предположим, что вы работаете с CPython, используйте cStringIO и создайте новые.

Python 3

Тот же код, только с seek(0).

>>> def truncate(sio):
...     sio.truncate(0)
...     sio.seek(0)
...     return sio
... 
>>> def new(sio):
...     return StringIO()
...

Когда пусто:

>>> from io import StringIO
>>> timeit(lambda: truncate(StringIO()))
0.9706327870007954
>>> timeit(lambda: new(StringIO()))
0.8734330690022034

С 3 Кбайтами данных в:

>>> timeit(lambda: truncate(StringIO('abc' * 1000)))
3.5271066290006274
>>> timeit(lambda: new(StringIO('abc' * 1000)))
3.3496507499985455

Итак, для Python 3, создающего новый, вместо повторного использования пустого, на 11% быстрее, а создание нового вместо повторного использования 3K - на 5% быстрее. Снова создайте новый StringIO, а не усекайте и ищите.

Ответ 2

Есть что-то важное отметить (по крайней мере, с Python 3.2):

искать (0) IS, необходимый перед усечением (0). Вот код без поиска (0):

from io import StringIO
s = StringIO()
s.write('1'*3)
print(repr(s.getvalue()))
s.truncate(0)
print(repr(s.getvalue()))
s.write('1'*3)
print(repr(s.getvalue()))

Какие выходы:

'111'
''
'\x00\x00\x00111'

с seek (0) до усечения, получаем ожидаемый результат:

'111'
''
'111'

Ответ 3

Как мне удалось оптимизировать мою обработку (чтение в кусках, обработка каждого фрагмента, запись обработанного потока в файл) из нескольких файлов в последовательности, так это то, что я повторно использую один и тот же экземпляр cStringIO.StringIO, но всегда reset() после используя, затем напишите на него, а затем truncate(). Делая это, я только усекаю часть в конце, которая мне не нужна для текущего файла. Это, похоже, дало мне увеличение производительности на 3%. Любой, кто более разбирается в этом, может подтвердить, действительно ли это оптимизирует распределение памяти.

sio = cStringIO.StringIO()
for file in files:
    read_file_chunks_and_write_to_sio(file, sio)
    sio.truncate()
    with open('out.bla', 'w') as f:
        f.write(sio.getvalue())
    sio.reset()