为啥 sys.stdout.write 被调用两次?

Posted

技术标签:

【中文标题】为啥 sys.stdout.write 被调用两次?【英文标题】:Why is sys.stdout.write called twice?为什么 sys.stdout.write 被调用两次? 【发布时间】:2021-07-13 21:24:22 【问题描述】:

我正在尝试将所有打印记录到日志文件中(并在实际消息之前添加时间戳,仅在日志文件中),但我无法弄清楚为什么 sys.stdout.write 被调用了两次。

import sys
from datetime import datetime

class Logger(object):
    def __init__(self, filename="Default.log"):
        self.stdout = sys.stdout
        self.log = open(filename, "w")

    def write(self, message):
        self.stdout.write(message)
        time_stamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
        self.log.write(f"time_stamp - message")
        self.log.flush()
    
    def flush(self):
        pass

sys.stdout = Logger("log_file.txt")
print("Hello world !")

终端输出:

Hello World !

log_file.txt 中的输出:

2021-04-19 18:43:14.800691 - Hello world !2021-04-19 18:43:14.800735 - 

我在这里缺少什么?在调用self.log.flush()之后再次调用write方法,但是这里是message=''

如果我忽略了 time_stamp 变量,它就像一个魅力,例如调用 self.log.write(message)

我当然可以只检查消息是否为空,但我真的很想在这里了解我的问题!

我的解决方案

@iBug 给了我答案!我不知道 print 确实以这种方式工作:一次写入数据调用,一次写入调用end 关键字,默认为\n

我的解决方案是添加一个变量 self._hidden_end 到 mthe Logger 类,该类初始化为 0,然后每次调用 write 时切换以检查它我应该在写入日志之前添加一个时间戳。

import sys
from datetime import datetime

class Logger(object):
    def __init__(self, filename="Default.log"):
        self.stdout = sys.stdout
        self.log = open(filename, "w")
        self._hidden_end = 0

    def write(self, message):
        self.stdout.write(message)
        if not self._hidden_end:
            time_stamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
            message = f"time_stamp - message"
        self._hidden_end ^= 1
        self.log.write(message)
        self.log.flush()
    
    def flush(self):
        pass

sys.stdout = Logger("log_file.txt")
print("Hello world !")

欢迎对我上面的解决方案提出任何想法! :)

【问题讨论】:

恐怕这就是print() 的工作方式——一次用于数据,一次用于换行(即隐藏的print(end='\n') 非常感谢 - 这解释了我的问题 :) @amkleist 您应该从问题中删除您的解决方案并将其作为答案发布。此外,“对我的解决方案的任何想法”都过于宽泛。 Stack Overflow 是针对关于您的代码的特定问题。对于一般的代码审查,codereview.stackexchange.com 可能是一个更好的地方,但记得先检查他们的on-topic 指南 @iBug 请添加答案而不是评论。请参阅Comment everywhere 上的我什么时候不应该发表评论?部分。 @SᴀᴍOnᴇᴌᴀ 这只是一个(希望是正确的)推测,我没有检查代码或搜索任何参考资料。这是 IMO 留下潜在但不完整的“答案”作为评论的一个很好的理由。 【参考方案1】:

您的感知结果表明print() 调用file.write 两次:一次用于数据,一次用于“结束内容”,默认情况下是换行符,可以用end 关键字参数覆盖。相关source code lines证实了这个猜测。

但是,这不是您应该依赖的。它完全是实现细节,对于另一个实现(例如 PyPy)可能会有所不同,并且可能随时更改,恕不另行通知,更不用说覆盖内置函数是另一种不好的做法。在没有令人信服的理由修改代码的其他部分以使用您的自定义日志记录工具的情况下,您应该避免这种做法。

如果你真的需要打猴子补丁,重写print() 函数会更安全,因为它有一个定义好的、可靠的接口。您可以导入builtins module 并将您的新打印功能分配给builtins.print,保持对日志处理程序调用的控制。

【讨论】:

以上是关于为啥 sys.stdout.write 被调用两次?的主要内容,如果未能解决你的问题,请参考以下文章

sys.stdout.write与sys.sterr.write

[Python]sys.stdin.readline(), sys.stdout.write(), sys.stdin.write()

sys.stdout.write 和 print 的区别?

python中sys.stdoutsys.stdin

python中sys.stdoutsys.stdin

python中print(obj) 与sys.stdout.write()的区别