J'ai créé un objet stringio et il contient du texte. Je voudrais effacer ses valeurs existantes et les réutiliser au lieu de les rappeler. Y a-t-il une manière de faire ça?
Ne vous embêtez pas à l'effacer, créez-en simplement un nouveau, c'est plus rapide.
Voici comment je trouverais de telles choses:
>>> 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.
Donc, vous voulez .truncate(0)
. Mais il est probablement moins cher (et plus facile) d'initialiser un nouveau StringIO. Voir ci-dessous pour les repères.
(Merci à tstone2077 pour soulignant la différence .)
>>> 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.
Il est important de noter avec cela que maintenant la position actuelle du fichier est inchangée , alors que la troncature à la taille zéro réinitialiserait la position dans le Python 2.
Ainsi, pour Python 2, vous n'avez besoin que
>>> from cStringIO import StringIO
>>> s = StringIO()
>>> s.write('foo')
>>> s.getvalue()
'foo'
>>> s.truncate(0)
>>> s.getvalue()
''
>>> s.write('bar')
>>> s.getvalue()
'bar'
Si vous faites cela en Python 3, vous n'obtiendrez pas le résultat que vous attendiez:
>>> 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'
Donc, dans Python 3, vous devez également réinitialiser la position:
>>> 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 vous utilisez la méthode truncate
dans Python 2, il est plus sûr d'appeler seek(0)
en même temps (avant ou après, cela n'a pas d'importance) afin que le code ne se casse pas lorsque vous le portez inévitablement vers Python 3. Et il y a une autre raison pour laquelle vous devez simplement créer un nouvel objet StringIO
!
>>> from timeit import timeit
>>> def truncate(sio):
... sio.truncate(0)
... return sio
...
>>> def new(sio):
... return StringIO()
...
A vide, avec StringIO:
>>> from StringIO import StringIO
>>> timeit(lambda: truncate(StringIO()))
3.5194039344787598
>>> timeit(lambda: new(StringIO()))
3.6533868312835693
Avec 3 Ko de données, avec StringIO:
>>> timeit(lambda: truncate(StringIO('abc' * 1000)))
4.3437709808349609
>>> timeit(lambda: new(StringIO('abc' * 1000)))
4.7179079055786133
Et la même chose avec 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
Donc, en ignorant les problèmes de mémoire potentiels (del oldstringio
), Il est plus rapide de tronquer un StringIO.StringIO
(3% plus rapide pour le vide, 8% plus rapide pour 3 Ko de données), mais c'est plus rapide ("plus rapide" aussi ) pour créer un nouveau cStringIO.StringIO
(8% plus rapide pour le vide, 10% plus rapide pour 3 Ko de données). Je vous recommande donc d'utiliser la plus simple, donc en supposant que vous travaillez avec CPython, utilisez cStringIO
et créez-en de nouvelles.
Le même code, juste avec seek(0)
inséré.
>>> def truncate(sio):
... sio.truncate(0)
... sio.seek(0)
... return sio
...
>>> def new(sio):
... return StringIO()
...
À vide:
>>> from io import StringIO
>>> timeit(lambda: truncate(StringIO()))
0.9706327870007954
>>> timeit(lambda: new(StringIO()))
0.8734330690022034
Avec 3 Ko de données dans:
>>> timeit(lambda: truncate(StringIO('abc' * 1000)))
3.5271066290006274
>>> timeit(lambda: new(StringIO('abc' * 1000)))
3.3496507499985455
Donc pour Python 3 en créer un nouveau au lieu de réutiliser un blanc est 11% plus rapide et en créer un nouveau au lieu de réutiliser un 3K est 5% plus rapide. Encore une fois, créez un nouveau StringIO
plutôt que de tronquer et de rechercher.
Il y a quelque chose d'important à noter (au moins avec Python 3.2):
cherche (0) EST nécessaire avant de tronquer (0). Voici du code sans la recherche (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()))
Quelles sorties:
'111'
''
'\x00\x00\x00111'
avec cherche (0) avant le tronqué, on obtient la sortie attendue:
'111'
''
'111'
La façon dont j'ai réussi à optimiser mon traitement (lire des morceaux, traiter chaque morceau, écrire le flux traité dans un fichier) de nombreux fichiers dans une séquence est que je réutilise la même instance cStringIO.StringIO
, Mais toujours reset()
après utilisation, puis écrivez dessus, puis truncate()
. En faisant cela, je ne tronque que la partie à la fin dont je n'ai pas besoin pour le fichier actuel. Cela semble m'avoir donné une augmentation de performance de ~ 3%. Quiconque est plus expert en la matière pourrait confirmer si cela optimise effectivement l'allocation de mémoire.
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()