Frage

Ich habe ein StringIO Objekt erstellt und es hat einen Text in ihm. Ich möchte seine vorhandenen Werte löschen und wiederverwenden, anstatt sie zu erinnern. Gibt es trotzdem, dies zu tun?

War es hilfreich?

Lösung

TL; DR

Stören Sie es nicht löschen, erstellen Sie einfach ein neues Geschäft-es ist schneller.

Die Methode

Python 2

Hier ist, wie ich so etwas finden würde aus:

>>> 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's 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's current size, the
    file remains unchanged.

Du willst also .truncate(0). Aber es ist wahrscheinlich billiger (und einfacher) einen neuen StringIO zu initialisieren. Siehe unten für die Benchmarks.

Python 3

(Danke an tstone2077 für den Unterschied Hinweis.)

>>> 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.

Es ist wichtig, mit diesem zu beachten Sie, dass jetzt die aktuelle Dateiposition unverändert ist , während der Größe Kürzen Null würde die Position in der Python-2-Variante zurück.

So für Python 2, brauchen Sie nur

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

Wenn Sie dies tun in Python 3, werden Sie nicht das Ergebnis bekommen Sie erwarten:

>>> 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'

So in Python 3 Sie auch die Position zurücksetzen müssen:

>>> 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'

Wenn die truncate Methode in Python 2-Code verwendet wird, ist es sicherer Anruf seek(0) zur gleichen Zeit (vor oder nach, es spielt keine Rolle), so dass der Code nicht brechen, wenn Sie unweigerlich Portierung auf Python 3. und es ist ein weiterer Grund, warum sollten Sie gerade ein neues StringIO Objekt erstellen!

Die Zeiten

Python 2

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

Wenn leer, mit StringIO:

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

Mit 3KB von Daten in, mit StringIO:

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

Und das gleiche mit 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

Also, ignoriert potenzielle Speicher Bedenken (del oldstringio), dann ist es schneller ein StringIO.StringIO (3% schneller leer, 8% schneller für 3KB von Daten) zu verkürzen, aber es ist schneller ( „fasterer“ auch) ein neues cStringIO.StringIO zu erstellen ( 8% schneller leer, 10% schneller für 3KB von Daten). Also würde ich empfehlen, nur mit der einfachsten einer so vorausgesetzt, Sie arbeiten mit CPython, Verwendung cStringIO und neue zu schaffen.

Python 3

Der gleiche Code, nur mit seek(0) Put-in.

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

Wenn leer:

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

Mit 3KB von Daten in:

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

Also für Python 3 einen neuen erstellt stattdessen einen leeren einen der Wiederverwendung ist 11% schneller und einen neuen erstellen, anstatt die Wiederverwendung ein 3K eines 5% schneller ist. Wieder eine neue StringIO schaffen, anstatt Kürzen und zu suchen.

Andere Tipps

Es ist etwas wichtig zu beachten, (zumindest mit Python 3.2):

suchen (0) erforderlich, bevor truncate (0). Hier ist ein Code ohne die seek (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()))

Welche Ausgänge:

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

mit seek (0) vor dem truncate wir die erwartete Ausgabe:

'111'
''
'111'

Wie ich es geschafft, meine Verarbeitung zu optimieren (in Blöcken zu lesen, verarbeiten jedes Chunk, Schreib verarbeitet ausströmen zu Datei) vieler Dateien in einer Folge ist, dass ich die gleiche cStringIO.StringIO Instanz wiederverwenden, aber es immer reset() nach der Anwendung, dann schreiben um es, und dann truncate(). Auf diese Weise bin ich Kürzen nur den Teil am Ende, dass ich für die aktuelle Datei nicht benötigen. Dies scheint zu haben mir ~ 3% Leistungssteigerung gegeben. Jeder, der mehr Experte auf diesem ist könnte bestätigen, ob dies tatsächlich Speicherzuweisung optimiert wird.

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()
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top