40

この方法でファイルを反復処理するときに、その理由を誰かが知っていますか?

入力:

f = open('test.txt', 'r')
for line in f:
    print "f.tell(): ",f.tell()

出力:

f.tell(): 8192
f.tell(): 8192
f.tell(): 8192
f.tell(): 8192

私は一貫してtell()から間違ったファイルインデックスを取得しますが、readlineを使用すると、tell()に適切なインデックスを取得します。

入力:

f = open('test.txt', 'r')
while True:
    line = f.readline()
    if (line == ''):
        break
    print "f.tell(): ",f.tell()

出力:

f.tell(): 103
f.tell(): 107
f.tell(): 115
f.tell(): 124

私はpython2.7.1BTWを実行しています。

4

3 に答える 3

62

開いているファイルを反復子として使用すると、先読みバッファを使用して効率が向上します。その結果、行をループすると、ファイル ポインターがファイル全体で大きなステップで進みます。

ファイルオブジェクトのドキュメントから:

for ループをファイルの行をループする最も効率的な方法 (非常に一般的な操作) にするために、このnext()メソッドは非表示の先読みバッファを使用します。先読みバッファを使用した結果、next()他のファイル メソッド ( などreadline()) との組み合わせが正しく機能しません。ただし、 を使用seek()してファイルを絶対位置に再配置すると、先読みバッファがフラッシュされます。

に依存する必要がある場合.tell()は、ファイル オブジェクトを反復子として使用しないでください。代わりにイテレータに変えることができ.readline()ます (パフォーマンスがいくらか低下します):

for line in iter(f.readline, ''):
    print f.tell()

これは、iter()関数 sentinelの引数を使用して、callable を反復子に変換します。

于 2013-01-03T18:41:31.503 に答える
12

答えは、Python 2.7 ソース コードの次の部分にあります ( fileobject.c)。

#define READAHEAD_BUFSIZE 8192

static PyObject *
file_iternext(PyFileObject *f)
{
    PyStringObject* l;

    if (f->f_fp == NULL)
        return err_closed();
    if (!f->readable)
        return err_mode("reading");

    l = readahead_get_line_skip(f, 0, READAHEAD_BUFSIZE);
    if (l == NULL || PyString_GET_SIZE(l) == 0) {
        Py_XDECREF(l);
        return NULL;
    }
    return (PyObject *)l;
}

ご覧のとおり、fileの iterator インターフェイスはファイルを 8KB のブロック単位で読み取ります。f.tell()これにより、がそのように動作する理由が説明されます。

ドキュメントは、パフォーマンス上の理由から行われていることを示唆しています (また、先読みバッファーの特定のサイズを保証するものではありません)。

于 2013-01-03T18:44:12.560 に答える