如何在Python中子类化文件类型?

13 投票
2 回答
9486 浏览
提问于 2025-04-15 12:41

我正在尝试在Python中对内置的file类进行子类化,以便为stdinstdout添加一些额外的功能。以下是我目前的代码:

class TeeWithTimestamp(file):
    """
    Class used to tee the output of a stream (such as stdout or stderr) into
    another stream, and to add a timestamp to each message printed.
    """

    def __init__(self, file1, file2):
        """Initializes the TeeWithTimestamp"""
        self.file1 = file1
        self.file2 = file2
        self.at_start_of_line = True

    def write(self, text):
        """Writes text to both files, prefixed with a timestamp"""

        if len(text):
            # Add timestamp if at the start of a line; also add [STDERR]
            # for stderr
            if self.at_start_of_line:
                now = datetime.datetime.now()
                prefix = now.strftime('[%H:%M:%S] ')
                if self.file1 == sys.__stderr__:
                    prefix += '[STDERR] '
                text = prefix + text

            self.file1.write(text)
            self.file2.write(text)

            self.at_start_of_line = (text[-1] == '\n')

我的目的是在每条消息的开头添加一个时间戳,并将所有内容记录到一个日志文件中。不过,我遇到的问题是,如果我这样做:

# log_file has already been opened
sys.stdout = TeeWithTimestamp(sys.stdout, log_file)

那么当我尝试执行print 'foo'时,就会出现ValueError: I/O operation on closed file的错误。我无法在我的__init__()方法中有意义地调用file.__init__(),因为我不想打开一个新文件,而且我也不能将self.closed = False,因为这是一个只读属性。

我该如何修改这个代码,以便我可以执行print 'foo',并且支持所有标准的file属性和方法呢?

2 个回答

3

你也可以不使用 super

class SuperFile(file):

    def __init__(self, *args, **kwargs):
        file.__init__(self, *args, **kwargs)

这样你就能顺利写代码了。

12

调用 file.__init__ 是可行的(比如在 '/dev/null' 上),但实际上没有什么用,因为你尝试重写的 write 方法对 print 语句来说并没有效果——因为 print 内部会直接调用真正的 file.write,当它发现 sys.stdout 是一个真正的 file 实例时(而你通过继承让它变成了这样)。

print 其实只需要 write 这个方法,所以如果你的类继承自 object 而不是 file,也是可以正常工作的。

如果你需要其他文件的方法(也就是说,print 不是你唯一要做的事情),那么最好自己实现这些方法。

撰写回答