来自Python脚本的无序输出
来自Python脚本的无序输出
Python解释器在sys.stdout
上是否默认启用了输出缓冲?
如果答案是肯定的,那么有哪些禁用它的方法?
迄今为止的建议:
- 使用
-u
命令行开关 - 将
sys.stdout
包装在在每次写入后刷新的对象中 - 设置
PYTHONUNBUFFERED
环境变量 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
在执行期间,是否还有其他方法可以在sys
/sys.stdout
中以编程方式设置一些全局标志?
如果您只想在使用print
时在特定写入后刷新,请参阅如何刷新print函数的输出?。
文章标题:Python中print函数输出乱序的原因及解决方法
在Python中,print函数输出乱序的问题是由于缓冲机制引起的。当我们使用print函数输出内容时,内容首先会被缓存起来,然后在合适的时机才会被写入到输出流中。这种缓冲机制的存在是为了提高程序的性能,减少IO操作的次数。
然而,在某些情况下,我们希望立即将内容输出到屏幕上,而不是等待缓冲区满或者遇到换行符时才输出。例如,当我们需要实时显示程序的运行进度或者调试信息时,就需要禁用缓冲机制,使得print函数能够立即输出内容。
Python提供了一个解决方案,即使用print函数的flush参数。从Python 3.3版本开始,我们可以在print函数中使用flush=True来禁用缓冲机制,使得内容立即输出到屏幕上。
下面是一个示例代码:
print('Hello World!', flush=True)
通过设置flush=True,print函数将立即将内容输出到屏幕上,而不需要等待缓冲区满或者遇到换行符。
需要注意的是,禁用缓冲机制会带来一定的性能损耗,因此在使用flush参数时需谨慎。只有在特定需要实时显示输出内容的情况下,才建议禁用缓冲机制。
总结起来,Python中print函数输出乱序的问题可以通过禁用缓冲机制来解决,即使用print函数的flush参数,并将其设置为True。这样可以保证内容立即输出到屏幕上,而不需要等待缓冲区满或者遇到换行符。但需要注意,禁用缓冲机制会带来一定的性能损耗,因此在使用时需谨慎权衡。
问题的出现原因:
- Python的print语句默认会进行输出缓冲,即将输出存储在缓冲区中,而不是立即将其发送到终端或管道。
- 这种缓冲机制在某些情况下会导致问题,特别是在输出被重定向到管道或文件时。
解决方法:
- 可以通过使用python -u
命令行选项或设置环境变量PYTHONUNBUFFERED来跳过整个Python进程的缓冲。
- 也可以将sys.stdout替换为另一个流,这个流在每次调用后都会执行刷新操作。具体实现可以使用以下代码:
class Unbuffered(object): def __init__(self, stream): self.stream = stream def write(self, data): self.stream.write(data) self.stream.flush() def writelines(self, datas): self.stream.writelines(datas) self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) import sys sys.stdout = Unbuffered(sys.stdout) print 'Hello'
- 原始的sys.stdout仍然可以通过sys.__stdout__访问,以防需要它。
其他注意事项:
- 输出缓冲的行为取决于输出流的类型,如果输出流是终端,则在每次遇到换行符时会执行刷新操作,但如果输出流是管道,则会进行缓冲。
- 在Cpython中,当使用for line in sys.stdin:
迭代输入时,循环体运行之前会收集多行输入,这会表现得像缓冲一样。可以改为使用while true: line = sys.stdin.readline()
来避免这种情况。
- 禁用输出缓冲的后果是性能提升,因为写入到控制台的速度相对较慢,所以批量写入可以减少开销。
- 可以使用iter()
代替while
循环,例如:for line in iter(pipe.readline, ''):
。在Python 3中,使用for line in pipe:
会尽快生成结果。
- 在一些特殊情况下,如在IDLE中,上述解决方法可能不起作用,因为sys.stdout已经被替换为其他对象,不允许执行刷新操作。
- 在运行CGI Python脚本时,这种解决方法非常有用,特别是在IIS上。同时,结合在web.config中设置responseBufferLimit="0"
,可以消除脚本输出的其他缓冲效果。
最后,如果想了解更多关于流缓冲的信息,可以参考一篇文章:eklitzke.org/stdout-buffering。
在Python中,当我们使用print()函数打印输出时,有时会遇到输出不及时的问题。这是因为默认情况下,print()函数将输出缓存在内存中,并在一定条件下才将其刷新到终端上。这可能会导致输出的顺序混乱或延迟显示。
为了解决这个问题,可以使用以下方法之一:
1. 在Python 3.3及以后的版本中,可以在print()函数中添加flush=True参数,强制将输出立即刷新到终端上。例如:print("Hello", flush=True)
。
2. 在Python 3中,可以通过重新打开sys.stdout文件描述符来实现无缓冲输出。具体做法是,将sys.stdout重新指向一个以写模式打开的文件,同时设置缓冲区大小为0。这可以通过以下代码实现:
# 重新以写模式打开stdout文件描述符,并设置缓冲区大小为0(无缓冲) import io, os, sys try: # Python 3,以二进制方式打开文件,然后包装为TextIOWrapper,设置write_through参数为True sys.stdout = io.TextIOWrapper(open(sys.stdout.fileno(), 'wb', 0), write_through=True) # 如果只在换行符上刷新即可,可以在Python 3.7及更高版本中直接调用以下方法: # sys.stdout.reconfigure(line_buffering=True) except TypeError: # Python 2 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
需要注意的是,上述代码只适用于Python 3中的情况。对于Python 2,可以使用os.fdopen()函数重新打开sys.stdout文件描述符,并设置缓冲区大小为0。
总结起来,要解决print()函数输出不及时的问题,可以通过在print()函数中添加flush=True参数,或者重新打开sys.stdout文件描述符来实现无缓冲输出。这样可以确保输出立即刷新到终端上,避免延迟显示或顺序混乱的问题。