4

データが書き込まれているときにファイルに直接フラッシュするファイルオブジェクトが必要で、次のように書きました。

class FlushingFileObject(file):
    def write(self,*args,**kwargs):
        return_val= file.write(self,*args,**kwargs)
        self.flush()
        return return_val

    def writelines(self,*args,**kwargs):
        return_val= file.writelines(self,*args,**kwargs)
        self.flush()
        return return_val

しかし興味深いことに、書き込み時にフラッシュしないので、これを含むいくつかのことを試しました:

class FlushingFileObject(object):
    def __init__(self,*args,**kwargs):
        self.file_object= file(*args,**kwargs)

    def __getattr__(self, item):
        return getattr(self.file_object,item)

    def write(self,*args,**kwargs):
        return_val= self.file_object.write(*args,**kwargs)
        self.file_object.flush()
        return return_val

    def writelines(self,*args,**kwargs):
        return_val= self.file_object.writelines(*args,**kwargs)
        self.file_object.flush()
        return return_val

これはフラッシュします。

fileこのインスタンスでサブクラス化が機能しないのはなぜですか?

4

2 に答える 2

9

素晴らしい質問です。

これは、Python がPython レベルのメソッドをバイパスして直接呼び出すことによりwrite、オブジェクトへの呼び出しを最適化するために発生します。filewritefputs

これを実際に確認するには、次のことを考慮してください。

$ cat file_subclass.py
import sys
class FileSubclass(file):
    def write(self, *a, **kw):
        raise Exception("write called!")
    writelines = write
sys.stdout = FileSubclass("/dev/null", "w")
print "foo"
sys.stderr.write("print succeeded!\n")
$ python print_magic.py
print succeeded!

writeメソッドが呼び出されませんでした。

オブジェクトが のサブクラスでない場合、file期待どおりに動作します。

$ cat object_subclass.py
import sys
class ObjectSubclass(object):
    def __init__(self):
        pass
    def write(self, *a, **kw):
        raise Exception("write called!")
    writelines = write
sys.stdout = ObjectSubclass()
print "foo"
sys.stderr.write("print succeeded!\n")
$ python object_subclass.py
Traceback (most recent call last):
  File "x.py", line 13, in <module>
    print "foo"
  File "x.py", line 8, in write
    raise Exception("write called!")
Exception: write called!

Python のソースを少し調べてみると、原因はステートメントPyFile_WriteStringによって呼び出される関数にあるようです。この関数は、書き込まれているオブジェクトが のインスタンスであるかどうかを確認し、そうである場合は、オブジェクトのメソッドをバイパスして直接呼び出します。printfilefputs

int
PyFile_WriteString(const char *s, PyObject *f)
{

    if (f == NULL) {
        /* … snip … */
    }
    else if (PyFile_Check(f)) { //-- `isinstance(f, file)`
        PyFileObject *fobj = (PyFileObject *) f;
        FILE *fp = PyFile_AsFile(f);
        if (fp == NULL) {
            err_closed();
            return -1;
        }
        FILE_BEGIN_ALLOW_THREADS(fobj)
        fputs(s, fp); //-- fputs, bypassing the Python object entirely
        FILE_END_ALLOW_THREADS(fobj)
        return 0;
    }
    else if (!PyErr_Occurred()) {
        PyObject *v = PyString_FromString(s);
        int err;
        if (v == NULL)
            return -1;
        err = PyFile_WriteObject(v, f, Py_PRINT_RAW);
        Py_DECREF(v);
        return err;
    }
    else
        return -1;
}
于 2012-09-27T18:40:34.947 に答える
-1

のドキュメントにfile.flush()は次のように書かれています:

注意 flush() は必ずしもファイルのデータをディスクに書き込みません。この動作を保証するには、flush() の後に os.fsync() を使用します。

os.fsync 呼び出しなしで、FlushingFileObject の最初のバージョンをテストしました。ファイルはフラッシュされませんでした。os.fsync(self.fileno())ファイルをフラッシュ挿入した後。次に、os.fsync 呼び出しを削除すると、ファイルがフラッシュされました。確かに、 os.fsync 呼び出しが必要だと思います。

于 2012-09-27T18:46:48.170 に答える