2012-08-09 13 views
32

Mam starszą wersję kodu ze starszą funkcją, która pobiera nazwę pliku jako argument i przetwarza zawartość pliku. Działająca faksymile kodu znajduje się poniżej.StringIO i kompatybilność z instrukcją 'with' (menedżer kontekstu)

Co chcę zrobić, to nie trzeba pisać na dysk z pewną treścią, którą generuję, aby użyć tej starszej funkcji, więc mógłbym użyć StringIO do stworzenia obiektu zamiast fizycznej nazwy pliku. Jednak to nie działa, jak widać poniżej.

Pomyślałem, że StringIO było drogą do tego. Czy ktoś może mi powiedzieć, czy istnieje sposób korzystania z tej funkcji legacy i przekazać go w argumencie, który nie jest plikiem na dysku, ale może być traktowany jako taki przez funkcję legacy? Funkcja starsza ma menedżera kontekstu with, który wykonuje pracę nad wartością parametru filename.

Jedyną rzeczą natknąłem w google było: http://bugs.python.org/issue1286, ale mi to nie pomoże ...

Kod

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

Wyjście

To wyjście w FileOnDisk.txt:

['This file is on disk.\n'] 

Jest to błąd:

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 
+3

cant „Otwórz” instancję StringIO –

Odpowiedz

52

Instancja StringIO instancja to już otwarty plik. Z drugiej strony, komenda open pobiera tylko nazwy plików, aby zwrócić otwarty plik. Instancja StringIO nie nadaje się jako nazwa pliku.

Nie trzeba również zamykać instancji StringIO, więc nie trzeba jej używać jako menedżera kontekstów.

Jeśli cały dotychczasowy kod może zająć nazwę pliku, to instancja StringIO nie jest drogą do zrobienia. Użyj tempfile module, aby wygenerować tymczasową nazwę pliku.

Oto przykład przy użyciu contextmanager zapewnienie pliku tymczasowego zostaje oczyszczony potem:

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) 
+0

Używam tego rozwiązania. Oto link do przykładowego kodu, który implementuje to bezpośrednio: http://pastie.org/4450354. Dziękuję wszystkim, którzy przyczynili się tutaj! – mpettis

+2

@mpettis: Zaktualizowałem swoją odpowiedź, aby podać przykład przy użyciu menedżera kontekstów, który utworzy tymczasowy plik i oczyści go za jednym razem. –

+0

To jest naprawdę elegancki sposób, aby poradzić sobie z tym ... Dzięki! – mpettis

4

można zdefiniować własną otwartą funkcję

fopen = open 
def open(fname,mode): 
    if hasattr(fname,"readlines"): return fname 
    else: return fopen(fname,mode) 

jednak ze chce zadzwonić __exit__ po jej zrobić i StringIO nie ma sposobu na wyjście ...

można określić niestandardową klasę do korzystania z tego otwarty

class MyStringIO: 
    def __init__(self,txt): 
     self.text = txt 
    def readlines(self): 
      return self.text.splitlines() 
    def __exit__(self): 
      pass 
+0

Niestety to nie rozwiązuje problemu, ponieważ musiałby być w środku poprzedniej funkcji – jdi

+0

czyżby to nie zastąpiło, gdyby było w tym samym pliku? –

+0

@jdi Myślę, że to może działać, jeśli zostało zdefiniowane przed starszą funkcją, tj. Po zaimportowaniu modułu starszego typu. –

Powiązane problemy