Отключить буферизацию вывода
Является ли буферизация вывода включена по умолчанию в интерпретаторе Python для sys.stdout
?
Если ответ положительный, какие способы его отключить?
Предложения до сих пор:
- Используйте переключатель командной строки
-u
- Оберните
sys.stdout
в объект, который сбрасывается после каждой записи
- Установить
PYTHONUNBUFFERED
env var
-
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
Есть ли другой способ установить глобальный флаг в sys
/sys.stdout
программно во время выполнения?
Ответы
Ответ 1
Из Ответ Магнуса Ликки в списке рассылки:
Вы можете пропустить буферизацию для целого python с использованием "python -u" (или #!/usr/bin/env python -u и т.д.) или установка переменной окружения PYTHONUNBUFFERED.
Вы также можете заменить 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'
Ответ 2
Я предпочел бы поместить свой ответ в Как очистить вывод печати Python? или в функцию печати Python, которая сбрасывает буфер, когда он вызван?, но поскольку они были отмечены как дубликаты этого (что я не согласен), я отвечу на него здесь.
Так как Python 3.3 print() поддерживает аргумент ключевого слова "flush" (см. документацию):
print('Hello World!', flush=True)
Ответ 3
# reopen stdout file descriptor with write mode
# and 0 as the buffer size (unbuffered)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
Кредиты: "Себастьян", где-то в списке рассылки Python.
Редактирование сторонних производителей
Не поддерживается в последних версиях Python 3
Ответ 4
Да, это так.
Вы можете отключить его в командной строке с помощью переключателя "-u".
В качестве альтернативы вы можете вызвать .flush() в sys.stdout для каждой записи (или обернуть ее с помощью объекта, который делает это автоматически)
Ответ 5
Это относится к Кристовау Д. Соузе, но я еще не мог прокомментировать.
Прямой способ использования аргумента flush
ключевого слова Python 3 для всегда имеет небуферизованный вывод:
import functools
print = functools.partial(print, flush=True)
печать всегда будет напрямую выгружать вывод (кроме flush=False
).
Обратите внимание, что (а) это отвечает на вопрос только частично, поскольку он не перенаправляет весь вывод. Но я полагаю, что print
является наиболее распространенным способом создания вывода в stdout
/stderr
в python, поэтому эти 2 строки покрывают, вероятно, большинство случаев использования.
Примечание (b), что он работает только в модуле / script, где вы его определили. Это может быть полезно при написании модуля, поскольку он не возится с sys.stdout
.
Python 2 не предоставляет аргумент flush
, но вы можете эмулировать функцию print
Python 3-type, описанную здесь fooobar.com/questions/7994/....
Ответ 6
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 - не такая хорошая идея, потому что там, где возникают небезопасные циклы, и вы можете проверить их.)
Ответ 7
В 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)
Ответ 8
Да, он включен по умолчанию. Вы можете отключить его, используя опцию -u в командной строке при вызове python.
Ответ 9
Вы также можете запустить Python с помощью stdbuf утилита:
stdbuf -oL python <script>
Ответ 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)
Ответ 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
.
Ответ 12
Вы можете создать небуферизованный файл и назначить этот файл sys.stdout.
import sys
myFile= open( "a.log", "w", 0 )
sys.stdout= myFile
Вы не можете волшебным образом изменить поставляемый системой stdout; поскольку он поставляется в вашу программу python операционной системой.
Ответ 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)
Ответ 14
(Я опубликовал комментарий, но он как-то потерялся. Итак, опять:)
-
Как я заметил, CPython (по крайней мере, в Linux) ведет себя по-разному в зависимости от того, где идет выход. Если он идет на tty, то после каждого "\n'
Если он идет на канал/процесс, он буферизуется, и вы можете использовать решения на основе flush()
или опцию -u, рекомендованную выше.
-
Немного связано с буферизацией вывода:
Если вы перебираете строки на входе с помощью
for line in sys.stdin:
...
то реализация для в CPython соберет ввод некоторое время, а затем запустит тело цикла для группы строк ввода. Если ваш script собирается записывать выходные данные для каждой строки ввода, это может выглядеть как буферизация вывода, но это фактически пакетная обработка, и поэтому ни одна из методов flush()
и т.д. Не поможет.
Интересно, что у вас нет такого поведения в pypy.
Чтобы этого избежать, вы можете использовать
while True:
line=sys.stdin.readline()
...
Ответ 15
В Python 3 вы можете "пропатчить" функцию печати, чтобы всегда отправлять flush = True:
_orig_print = print
def print(*args, **kwargs):
_orig_print(*args, flush=True, **kwargs)
Как указано в комментарии, вы можете упростить это, связав параметр flush со значением через functools.partial
:
print = functools.partial(print, flush=True)
Ответ 16
Одним из способов получения небуферизованного вывода было бы использовать sys.stderr
вместо sys.stdout
или просто вызвать sys.stdout.flush()
, чтобы явно принудительно записать запись.
Вы можете легко перенаправить все, напечатав:
import sys; sys.stdout = sys.stderr
print "Hello World!"
Или перенаправить только для конкретного оператора print
:
print >>sys.stderr, "Hello World!"
В reset stdout вы можете просто сделать:
sys.stdout = sys.__stdout__