J'ai du code hérité avec une fonction héritée qui prend un nom de fichier comme argument et traite le contenu du fichier. Un fac-similé de travail du code est ci-dessous.
Ce que je veux faire n'est pas d'avoir à écrire sur le disque avec du contenu que je génère afin d'utiliser cette fonction héritée, donc j'ai pensé que je pourrais utiliser StringIO
pour créer un objet à la place du nom de fichier physique. Cependant, cela ne fonctionne pas, comme vous pouvez le voir ci-dessous.
Je pensais que StringIO
était la solution. Quelqu'un peut-il me dire s'il existe un moyen d'utiliser cette fonction héritée et de lui transmettre quelque chose dans l'argument qui n'est pas un fichier sur le disque mais qui peut être traité comme tel par la fonction héritée? La fonction héritée a le gestionnaire de contexte with
qui travaille sur la valeur du paramètre filename
.
La seule chose que j'ai rencontrée dans google était: http://bugs.python.org/issue1286 , mais cela ne m'a pas aidé ...
Code
from pprint import pprint
import StringIO
# Legacy Function
def processFile(filename):
with open(filename, 'r') as fh:
return fh.readlines()
# This works
print 'This is the output of FileOnDisk.txt'
pprint(processFile('c:/temp/FileOnDisk.txt'))
print
# This fails
plink_data = StringIO.StringIO('StringIO data.')
print 'This is the error.'
pprint(processFile(plink_data))
Sortie
Il s'agit de la sortie dans FileOnDisk.txt
:
['This file is on disk.\n']
Voici l'erreur:
Traceback (most recent call last):
File "C:\temp\test.py", line 20, in <module>
pprint(processFile(plink_data))
File "C:\temp\test.py", line 6, in processFile
with open(filename, 'r') as fh:
TypeError: coercing to Unicode: need string or buffer, instance found
Une instance de StringIO
est déjà un fichier ouvert. La commande open
, en revanche, ne prend que les noms de fichiers, pour renvoyer un fichier ouvert. Une instance StringIO
ne convient pas comme nom de fichier.
De plus, vous n'avez pas besoin de fermer une instance StringIO
, il n'est donc pas nécessaire de l'utiliser comme gestionnaire de contexte non plus.
Si tout votre code hérité peut prendre un nom de fichier, alors une instance StringIO
n'est pas la solution. Utilisez le module tempfile
pour générer un nom de fichier temporaire à la place.
Voici un exemple d'utilisation d'un gestionnaire de contexte pour garantir que le fichier temporaire est nettoyé par la suite:
import os
import tempfile
from contextlib import contextmanager
@contextmanager
def tempinput(data):
temp = tempfile.NamedTemporaryFile(delete=False)
temp.write(data)
temp.close()
try:
yield temp.name
finally:
os.unlink(temp.name)
with tempinput('Some data.\nSome more data.') as tempfilename:
processFile(tempfilename)
Vous pouvez également passer à la nouvelle infrastructure Python 3 offerte par le module io
(disponible dans Python 2 et 3), où - io.BytesIO
est le remplacement le plus robuste de StringIO.StringIO
/cStringIO.StringIO
. Cet objet ne prend pas en charge l'utilisation en tant que gestionnaire de contexte (mais ne peut toujours pas être transmis à open()
).
vous pouvez définir votre propre fonction ouverte
fopen = open
def open(fname,mode):
if hasattr(fname,"readlines"): return fname
else: return fopen(fname,mode)
cependant avec veut appeler __exit__ après son fait et StringIO n'a pas de méthode de sortie ...
vous pouvez définir une classe personnalisée à utiliser avec cette ouverture
class MyStringIO:
def __init__(self,txt):
self.text = txt
def readlines(self):
return self.text.splitlines()
def __exit__(self):
pass
Celui-ci est basé sur le python doc de contextmanager
Il s'agit simplement d'encapsuler StringIO avec un contexte simple, et lorsque exit est appelé, il reviendra au point de rendement et fermera correctement le StringIO. Cela évite d'avoir à créer un fichier temporaire, mais avec une grande chaîne, cela consommera toujours de la mémoire, car StringIO met cette chaîne en mémoire tampon. Cela fonctionne bien dans la plupart des cas où vous savez que les données de chaîne ne seront pas longues
from contextlib import contextmanager
@contextmanager
def buildStringIO(strData):
from cStringIO import StringIO
try:
fi = StringIO(strData)
yield fi
finally:
fi.close()
Ensuite, vous pouvez faire:
with buildStringIO('foobar') as f:
print(f.read()) # will print 'foobar'