Отключить буферизацию вывода

Является ли буферизация вывода включена по умолчанию в интерпретаторе 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__