53

ファイル名を引数として取り、ファイルの内容を処理するレガシー関数を備えたレガシーコードがいくつかあります。コードの実用的な複製は以下のとおりです。

私がやりたいのは、このレガシー関数を使用するために生成したコンテンツをディスクに書き込む必要がないためStringIO、物理ファイル名の代わりにオブジェクトを作成するために使用できます。ただし、以下に示すように、これは機能しません。

StringIO私はこれで行く方法だと思いました。このレガシー関数を使用して、ディスク上のファイルではないが、レガシー関数によってそのように扱うことができる何かを引数に渡す方法があるかどうか誰かに教えてもらえますか?レガシー関数には、パラメーター値withの処理を行うコンテキストマネージャーがあります。filename

私がグーグルで出くわした1つのことは:http://bugs.python.org/issue1286でした、しかしそれは私を助けませんでした...

コード

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

出力

これは次の出力ですFileOnDisk.txt

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

これはエラーです:

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
4

3 に答える 3

75

StringIOインスタンスすでに開いているファイルです。一方open、コマンドはファイル名のみを受け取り、開いているファイルを返します。StringIOインスタンスはファイル名として適切ではありません。

また、StringIOインスタンスを閉じる必要がないため、コンテキスト マネージャーとして使用する必要もありません。インスタンスを閉じると、割り当てられたメモリが解放されますが、ガベージ コレクタにオブジェクトをリープさせるだけです。いずれにせよ、オブジェクトへの参照を保持したままメモリを確実に解放したい場合は、contextlib.closing()コンテキスト マネージャーがオブジェクトを閉じる処理を行うことができます。

レガシ コードで使用できるのがファイル名だけである場合、StringIOインスタンスは適していません。tempfile代わりにモジュールを使用して、一時的なファイル名を生成してください。

次に、contextmanager を使用して一時ファイルが後で確実にクリーンアップされるようにする例を示します。

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)

ioモジュールによって提供される新しい Python 3 インフラストラクチャ (Python 2 および 3 で利用可能) にio.BytesIO切り替えることもできStringIO.StringIOますcStringIO.StringIO。このオブジェクトは、コンテキスト マネージャーとしての使用をサポートしています (ただし、 に渡すことはできませんopen())。

于 2012-08-09T22:10:49.883 に答える
6

独自の open 関数を定義できます

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

ただし、完了後に __exit__ を呼び出したいのですが、StringIO には exit メソッドがありません...

このオープンで使用するカスタム クラスを定義できます。

class MyStringIO:
     def __init__(self,txt):
         self.text = txt
     def readlines(self):
          return self.text.splitlines()
     def __exit__(self):
          pass
于 2012-08-09T22:05:45.040 に答える