为什么在取消链接后调用我的 revectored sys.stdout.write() 方法?

Why does my revectored sys.stdout.write() method get called after I've unlinked it?

我写了一个 tee() class,它根据 Triptych 在 的回答将 stdout 重定向到终端和文件

它在我的程序运行的前几次有效,但第三次我得到:

  File "C:\Users\Dave\data\Code\Python\lib\nerdlib.py", line 351, in write
    with open(self.name, "a",  encoding="utf-8") as f:

TypeError: expected str, bytes or os.PathLike object, not NoneType

似乎即使我做了 sys.stdout = self.old_stdout(将执行向量恢复到它开始的地方),我的 write 方法仍然以某种方式被调用(在这种情况下会出现错误)。

但是为什么我的方法仍然被调用?

此错误发生在 Spyder 开发环境中 - 不确定是否相关。

代码如下 - 很简单。

# based on Triptych's answer at 
class tee():
    ''' Tees stdout to both a log file and the terminal.

        if append is True (default), appends to existing file

        if stderr is True (default), tees stederr to same file

        Usage:
            t = tee("filename.txt")
            ...
            print(1, 2, 3)
            ...
            t.__del__() # returns sys.stdout to original state
    '''

    def __init__(self, filepath, append=True, stderr=True):

        self.old_stdout = sys.stdout
        self.old_stderr = sys.stderr
        self.name = filepath

        if (not append) or (not os.path.exists(self.name)):
            makeEmptyFile(self.name)

        sys.stdout = self

        if stderr:
            sys.stderr = self

    def write(self, text):

        self.old_stdout.write(text)

        # open file, write, then close it again (slow, but safe - permits other processes to write to same file)
        with open(self.name, "a",  encoding="utf-8") as f:
           f.write(text)

    def flush(self):
        pass

    def stop(self):
        sys.stdout = self.old_stdout
        sys.stdout = self.old_stderr
        self.name = None

    def __del__(self):
        self.stop()

这里的危险是设置 sys.stdout = sys.old_stdout 会阻止代码在本地替换上获得 new 句柄,但不会阻止任何 旧的 句柄无法工作。

举个例子:

class MyThread(threading.Thread):
    def __init__(self, log_dest=sys.stdout):
        self.log_dest = log_dest
    def run(self):
        # ...do something-or-other, and occasionally...
        self.log_dest.write('Hey, something happened')

因为 log_dest 在 MyThread 初始化时 被分配了一个值 ,将 sys.stdout 改回其旧值并不会阻止它仍然尝试使用您现在无效的对象。