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

Включена ли буферизация вывода по умолчанию в интерпретаторе Python для sys.stdout?

Если ответ положительный, какие есть способы его отключения?

Предложения на данный момент:

  1. Использовать -u переключатель командной строки
  2. Заворачивать sys.stdout в объекте, который мигает после каждой записи
  3. Задавать PYTHONUNBUFFERED env var
  4. sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

Есть ли другой способ установить глобальный флаг в sys/sys.stdout программно во время исполнения?

16 ответов

Решение

От Магнуса Лицки ответ на рассылку:

Вы можете пропустить буферизацию для всего процесса 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'

Я предпочел бы поместить свой ответ в Как очистить вывод печати Python? или в функции печати Python, которая очищает буфер при вызове?, но так как они были помечены как дубликаты этого (что я не согласен), я отвечу на это здесь.

Начиная с Python 3.3 print() поддерживает ключевое слово аргумент "flush" ( см. Документацию):

print('Hello World!', flush=True)
# 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

Да, это.

Вы можете отключить его в командной строке с помощью ключа "-u".

В качестве альтернативы, вы можете вызывать.flush() для sys.stdout при каждой записи (или обернуть его объектом, который делает это автоматически)

Это относится к ответу Кристована Д. Соузы, но я пока не могу комментировать.

Прямой способ использования flush Ключевое слово аргумента Python 3 для того, чтобы всегда иметь небуферизованный вывод:

import functools
print = functools.partial(print, flush=True)

после этого печать всегда сбрасывает вывод напрямую (кроме flush=False дано).

Обратите внимание, (а) что это отвечает на вопрос только частично, так как не перенаправляет весь вывод. Но я думаю print является наиболее распространенным способом создания выходных данных для stdout/stderr в Python, поэтому эти 2 строки охватывают, вероятно, большинство случаев использования.

Обратите внимание (б), что он работает только в модуле / скрипте, где вы его определили. Это может быть полезно при написании модуля, так как он не портит sys.stdout,

Python 2 не обеспечивает flush аргумент, но вы могли бы подражать Python 3-типа print функционировать, как описано здесь /questions/22538236/nuzhna-versiya-print-dlya-python-34-iz-future/22538248#22538248.

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 не очень хорошая идея, потому что именно там создаются неразрушимые циклы, и вы можете проверить их.)

Следующие работы в 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)

В Python 3 вы можете "обезьянько-патчить" функцию печати, чтобы всегда отправлять flush=True:

_orig_print = print

def print(*args, **kwargs):
    _orig_print(*args, flush=True, **kwargs)

Да, он включен по умолчанию. Вы можете отключить его, используя опцию -u в командной строке при вызове python.

Вы также можете запустить Python с помощью утилиты stdbuf:

stdbuf -oL python <script>

Вы также можете использовать 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)

Один из способов получить небуферизованный вывод - использовать 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__

Вы можете создать небуферизованный файл и назначить этот файл для sys.stdout.

import sys 
myFile= open( "a.log", "w", 0 ) 
sys.stdout= myFile

Вы не можете волшебным образом изменить предоставляемый системой стандартный вывод; так как он поставляется вашей программой Python операционной системой.

Можно только переопределить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,

Вариант, который работает без сбоев (по крайней мере, на 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)

(Я разместил комментарий, но он как-то потерялся. Итак, снова:)

  1. Как я заметил, CPython (по крайней мере, в Linux) ведет себя по-разному в зависимости от того, куда выводится. Если он идет в tty, то вывод сбрасывается после каждого\n'
    Если он идет в канал / процесс, то он буферизуется, и вы можете использовать flush() основанные решения или опция -u, рекомендованная выше.

  2. Немного связано с буферизацией вывода:
    Если вы перебираете строки на входе с

    for line in sys.stdin:
    ...

затем реализация for в CPython будет некоторое время собирать входные данные, а затем выполнять тело цикла для набора строк ввода. Если ваш сценарий собирается записать вывод для каждой строки ввода, это может выглядеть как буферизация вывода, но на самом деле это пакетная обработка, и, следовательно, ни одна из flush()и т.д. методы помогут этому. Интересно, что у вас нет такого поведения в pypy. Чтобы избежать этого, вы можете использовать

while True: line=sys.stdin.readline()
...

Другие вопросы по тегам