问题:禁用输出缓冲
默认情况下,Python的解释器是否启用输出缓冲sys.stdout
?
如果答案是肯定的,那么有什么方法可以禁用它?
到目前为止的建议:
- 使用
-u
命令行开关
- 包装
sys.stdout
每次写入后刷新的对象
- 设置环境
PYTHONUNBUFFERED
变量
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
还有其他方法可以在执行过程中以编程方式在sys
/中设置一些全局标志sys.stdout
吗?
Is output buffering enabled by default in Python’s interpreter for sys.stdout
?
If the answer is positive, what are all the ways to disable it?
Suggestions so far:
- Use the
-u
command line switch
- Wrap
sys.stdout
in an object that flushes after every write
- Set
PYTHONUNBUFFERED
env var
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
Is there any other way to set some global flag in sys
/sys.stdout
programmatically during execution?
回答 0
从Magnus Lycka的邮件列表中答复:
您可以使用“ python -u”(或#!/ usr / bin / env 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'
From Magnus Lycka answer on a mailing list:
You can skip buffering for a whole
python process using “python -u”
(or#!/usr/bin/env python -u etc) or by
setting the environment variable
PYTHONUNBUFFERED.
You could also replace sys.stdout with
some other stream like wrapper which
does a flush after every call.
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'
回答 1
回答 2
# reopen stdout file descriptor with write mode
# and 0 as the buffer size (unbuffered)
import io, os, sys
try:
# Python 3, open as binary, then wrap in a TextIOWrapper with write-through.
sys.stdout = io.TextIOWrapper(open(sys.stdout.fileno(), 'wb', 0), write_through=True)
# If flushing on newlines is sufficient, as of 3.7 you can instead just call:
# sys.stdout.reconfigure(line_buffering=True)
except TypeError:
# Python 2
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
鸣谢:“ Sebastian”,在Python邮件列表上的某处。
# reopen stdout file descriptor with write mode
# and 0 as the buffer size (unbuffered)
import io, os, sys
try:
# Python 3, open as binary, then wrap in a TextIOWrapper with write-through.
sys.stdout = io.TextIOWrapper(open(sys.stdout.fileno(), 'wb', 0), write_through=True)
# If flushing on newlines is sufficient, as of 3.7 you can instead just call:
# sys.stdout.reconfigure(line_buffering=True)
except TypeError:
# Python 2
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
Credits: “Sebastian”, somewhere on the Python mailing list.
回答 3
是的。
您可以使用“ -u”开关在命令行上禁用它。
或者,您可以在每次写入时在sys.stdout上调用.flush()(或将其包装为自动执行此操作的对象)
Yes, it is.
You can disable it on the commandline with the “-u” switch.
Alternatively, you could call .flush() on sys.stdout on every write (or wrap it with an object that does this automatically)
回答 4
这与克里斯托瓦·索萨(CristóvãoD. Sousa)的答案有关,但我无法发表评论。
为了始终具有未缓冲的输出而使用Python 3的flush
关键字参数的直接方法是:
import functools
print = functools.partial(print, flush=True)
之后,打印将始终直接刷新输出(flush=False
给出的除外)。
请注意,(a)这只能部分回答问题,因为它不会重定向所有输出。但是我想这print
是在python中创建输出到stdout
/ 的最常见方法stderr
,因此这两行可能涵盖了大多数用例。
注意(b)它仅在定义它的模块/脚本中起作用。编写模块时这可能会很好,因为它不会与混淆sys.stdout
。
Python 2不提供flush
参数,但是您可以仿真Python 3型print
函数,如此处https://stackoverflow.com/a/27991478/3734258所述。
This relates to Cristóvão D. Sousa’s answer, but I couldn’t comment yet.
A straight-forward way of using the flush
keyword argument of Python 3 in order to always have unbuffered output is:
import functools
print = functools.partial(print, flush=True)
afterwards, print will always flush the output directly (except flush=False
is given).
Note, (a) that this answers the question only partially as it doesn’t redirect all the output. But I guess print
is the most common way for creating output to stdout
/stderr
in python, so these 2 lines cover probably most of the use cases.
Note (b) that it only works in the module/script where you defined it. This can be good when writing a module as it doesn’t mess with the sys.stdout
.
Python 2 doesn’t provide the flush
argument, but you could emulate a Python 3-type print
function as described here https://stackoverflow.com/a/27991478/3734258 .
回答 5
def disable_stdout_buffering():
# Appending to gc.garbage is a way to stop an object from being
# destroyed. If the old sys.stdout is ever collected, it will
# close() stdout, which is not good.
gc.garbage.append(sys.stdout)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
# Then this will give output in the correct order:
disable_stdout_buffering()
print "hello"
subprocess.call(["echo", "bye"])
如果不保存旧的sys.stdout,disable_stdout_buffering()并不是幂等的,并且多次调用将导致如下错误:
Traceback (most recent call last):
File "test/buffering.py", line 17, in <module>
print "hello"
IOError: [Errno 9] Bad file descriptor
close failed: [Errno 9] Bad file descriptor
另一种可能性是:
def disable_stdout_buffering():
fileno = sys.stdout.fileno()
temp_fd = os.dup(fileno)
sys.stdout.close()
os.dup2(temp_fd, fileno)
os.close(temp_fd)
sys.stdout = os.fdopen(fileno, "w", 0)
(添加到gc.garbage并不是一个好主意,因为这是放置不可释放的循环的地方,您可能需要检查这些循环。)
def disable_stdout_buffering():
# Appending to gc.garbage is a way to stop an object from being
# destroyed. If the old sys.stdout is ever collected, it will
# close() stdout, which is not good.
gc.garbage.append(sys.stdout)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
# Then this will give output in the correct order:
disable_stdout_buffering()
print "hello"
subprocess.call(["echo", "bye"])
Without saving the old sys.stdout, disable_stdout_buffering() isn’t idempotent, and multiple calls will result in an error like this:
Traceback (most recent call last):
File "test/buffering.py", line 17, in <module>
print "hello"
IOError: [Errno 9] Bad file descriptor
close failed: [Errno 9] Bad file descriptor
Another possibility is:
def disable_stdout_buffering():
fileno = sys.stdout.fileno()
temp_fd = os.dup(fileno)
sys.stdout.close()
os.dup2(temp_fd, fileno)
os.close(temp_fd)
sys.stdout = os.fdopen(fileno, "w", 0)
(Appending to gc.garbage is not such a good idea because it’s where unfreeable cycles get put, and you might want to check for those.)
回答 6
以下在Python 2.6、2.7和3.2中有效:
import os
import sys
buf_arg = 0
if sys.version_info[0] == 3:
os.environ['PYTHONUNBUFFERED'] = '1'
buf_arg = 1
sys.stdout = os.fdopen(sys.stdout.fileno(), 'a+', buf_arg)
sys.stderr = os.fdopen(sys.stderr.fileno(), 'a+', buf_arg)
The following works in Python 2.6, 2.7, and 3.2:
import os
import sys
buf_arg = 0
if sys.version_info[0] == 3:
os.environ['PYTHONUNBUFFERED'] = '1'
buf_arg = 1
sys.stdout = os.fdopen(sys.stdout.fileno(), 'a+', buf_arg)
sys.stderr = os.fdopen(sys.stderr.fileno(), 'a+', buf_arg)
回答 7
是的,默认情况下启用。您可以在调用python时通过在命令行上使用-u选项禁用它。
Yes, it is enabled by default. You can disable it by using the -u option on the command line when calling python.
回答 8
您还可以使用stdbuf实用程序运行Python :
stdbuf -oL python <script>
You can also run Python with stdbuf utility:
stdbuf -oL python <script>
回答 9
在Python 3中,您可以使用Monkey补丁打印功能,以始终发送flush = True:
_orig_print = print
def print(*args, **kwargs):
_orig_print(*args, flush=True, **kwargs)
如注释中所指出的,您可以通过以下方式将flush参数绑定到一个值来简化此操作functools.partial
:
print = functools.partial(print, flush=True)
In Python 3, you can monkey-patch the print function, to always send flush=True:
_orig_print = print
def print(*args, **kwargs):
_orig_print(*args, flush=True, **kwargs)
As pointed out in a comment, you can simplify this by binding the flush parameter to a value, via functools.partial
:
print = functools.partial(print, flush=True)
回答 10
您也可以使用fcntl即时更改文件标志。
fl = fcntl.fcntl(fd.fileno(), fcntl.F_GETFL)
fl |= os.O_SYNC # or os.O_DSYNC (if you don't care the file timestamp updates)
fcntl.fcntl(fd.fileno(), fcntl.F_SETFL, fl)
You can also use fcntl to change the file flags in-fly.
fl = fcntl.fcntl(fd.fileno(), fcntl.F_GETFL)
fl |= os.O_SYNC # or os.O_DSYNC (if you don't care the file timestamp updates)
fcntl.fcntl(fd.fileno(), fcntl.F_SETFL, fl)
回答 11
可以使用调用的方法仅 覆盖的write
方法。建议的方法实现如下。sys.stdout
flush
def write_flush(args, w=stdout.write):
w(args)
stdout.flush()
w
参数的默认值将保留原始write
方法引用。在 write_flush
定义之后,原始文件write
可能会被覆盖。
stdout.write = write_flush
该代码假定以stdout
这种方式导入from sys import stdout
。
It is possible to override only write
method of sys.stdout
with one that calls flush
. Suggested method implementation is below.
def write_flush(args, w=stdout.write):
w(args)
stdout.flush()
Default value of w
argument will keep original write
method reference. After write_flush
is defined, the original write
might be overridden.
stdout.write = write_flush
The code assumes that stdout
is imported this way from sys import stdout
.
回答 12
您可以创建一个无缓冲的文件,并将该文件分配给sys.stdout。
import sys
myFile= open( "a.log", "w", 0 )
sys.stdout= myFile
您无法神奇地更改系统提供的标准输出;因为它是由操作系统提供给您的python程序的。
You can create an unbuffered file and assign this file to sys.stdout.
import sys
myFile= open( "a.log", "w", 0 )
sys.stdout= myFile
You can’t magically change the system-supplied stdout; since it’s supplied to your python program by the OS.
回答 13
在不崩溃的情况下起作用的变体(至少在win32上; python 2.7,ipython 0.12)然后随后被调用(多次):
def DisOutBuffering():
if sys.stdout.name == '<stdout>':
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
if sys.stderr.name == '<stderr>':
sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 0)
Variant that works without crashing (at least on win32; python 2.7, ipython 0.12) then called subsequently (multiple times):
def DisOutBuffering():
if sys.stdout.name == '<stdout>':
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
if sys.stderr.name == '<stderr>':
sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 0)
回答 14
(我发表了评论,但由于某种原因迷失了。因此,再次:)
正如我注意到的那样,CPython(至少在Linux上)的行为取决于输出的位置。如果是tty,则在每个’之后都将刷新输出。\n'
如果是管道/进程,则将其缓冲,您可以使用flush()
基于基础的解决方案或上面建议的-u选项。
与输出缓冲稍微相关:
如果您使用来遍历输入中的行
for line in sys.stdin:
…
然后在CPython中的for实现将收集输入一段时间,然后对一堆输入行执行循环主体。如果您的脚本要为每条输入行写输出,这可能看起来像输出缓冲,但实际上是批处理,因此,等技术都无法提供帮助。有趣的是,您在pypy中没有这种行为。为了避免这种情况,您可以使用flush()
while True:
line=sys.stdin.readline()
…
(I’ve posted a comment, but it got lost somehow. So, again:)
As I noticed, CPython (at least on Linux) behaves differently depending on where the output goes. If it goes to a tty, then the output is flushed after each ‘\n'
If it goes to a pipe/process, then it is buffered and you can use the flush()
based solutions or the -u option recommended above.
Slightly related to output buffering:
If you iterate over the lines in the input with
for line in sys.stdin:
…
then the for implementation in CPython will collect the input for a while and then execute the loop body for a bunch of input lines. If your script is about to write output for each input line, this might look like output buffering but it’s actually batching, and therefore, none of the flush()
, etc. techniques will help that.
Interestingly, you don’t have this behaviour in pypy.
To avoid this, you can use
while True:
line=sys.stdin.readline()
…
回答 15
获得无缓冲输出的一种方法是使用sys.stderr
而不是sys.stdout
或简单地调用sys.stdout.flush()
以显式强制发生写入。
您可以通过执行以下操作轻松重定向所有打印内容:
import sys; sys.stdout = sys.stderr
print "Hello World!"
或仅针对特定print
语句进行重定向:
print >>sys.stderr, "Hello World!"
要重置标准输出,您可以执行以下操作:
sys.stdout = sys.__stdout__
One way to get unbuffered output would be to use sys.stderr
instead of sys.stdout
or to simply call sys.stdout.flush()
to explicitly force a write to occur.
You could easily redirect everything printed by doing:
import sys; sys.stdout = sys.stderr
print "Hello World!"
Or to redirect just for a particular print
statement:
print >>sys.stderr, "Hello World!"
To reset stdout you can just do:
sys.stdout = sys.__stdout__