¿Cómo borro un objeto stringio?

Tengo un objeto stringio creado y tiene algo de texto. Me gustaría borrar sus valores existentes y reutilizarlos en lugar de recuperarlos. ¿Hay alguna forma de hacer esto?

TL; DR

No se moleste en borrarlo, solo cree uno nuevo, es más rápido.

El método

Python 2

Así es como me gustaría averiguar tales cosas:

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

Entonces, quieres .truncate(0) . Pero probablemente sea más barato (y más fácil) inicializar un nuevo StringIO. Vea a continuación los puntos de referencia.

Python 3

(Gracias a tstone2077 por señalar la diferencia ).

 >>> 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 importante tener en cuenta que ahora la posición actual del archivo no se modifica , mientras que el truncado al tamaño cero restablecería la posición en la variante de Python 2.

Por lo tanto, para Python 2, solo necesitas

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

Si haces esto en Python 3, no obtendrás el resultado que esperabas:

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

Así que en Python 3 también necesitas restablecer la posición:

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

Si usa el método truncate en el código de Python 2, es más seguro llamar a seek(0) al mismo tiempo (antes o después, no importa), de modo que el código no se rompa cuando inevitablemente lo transfiera a Python 3. ¡Y hay otra razón por la que debes crear un nuevo objeto StringIO !

Veces

Python 2

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

Cuando esté vacío, con StringIO:

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

Con 3KB de datos en, con StringIO:

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

Y lo mismo con 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 

Por lo tanto, ignorando los problemas de memoria potenciales ( del oldstringio ), es más rápido truncar un StringIO.StringIO (3% más rápido por vacío, 8% más rápido por 3KB de datos), pero también es más rápido (“más rápido”) crear un nuevo cStringIO.StringIO (8% más rápido que vacío, 10% más rápido para 3 KB de datos). Por lo tanto, recomiendo usar el más fácil, así que suponiendo que esté trabajando con CPython, use cStringIO y cree otros nuevos.

Python 3

El mismo código, solo con seek(0) introducido.

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

Cuando vacio

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

Con 3KB de datos en:

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

Entonces, para Python 3, crear uno nuevo en lugar de reutilizar uno en blanco es un 11% más rápido y crear uno nuevo en lugar de reutilizar un 3K es un 5% más rápido. De nuevo, crea un nuevo StringIO lugar de StringIO y buscarlo.

Hay algo importante a tener en cuenta (al menos con Python 3.2):

buscar (0) se necesita antes de truncar (0). Aquí hay un código sin buscar (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())) 

Qué salidas:

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

con seek (0) antes del truncado, obtenemos el resultado esperado:

 '111' '' '111' 

La forma en que logré optimizar mi procesamiento (leer en fragmentos, procesar cada fragmento, escribir la secuencia procesada en un archivo) de muchos archivos en una secuencia es que reutilizo la misma instancia de cStringIO.StringIO , pero siempre lo reset() después de usarlo. escríbelo y luego truncate() . Al hacer esto, solo estoy truncando la parte al final que no necesito para el archivo actual. Esto parece haberme dado un aumento de rendimiento de ~ 3%. Cualquiera que sea más experto en esto podría confirmar si esto realmente optimiza la asignación de memoria.

 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()