Отключить буферизацию вывода - PullRequest
456 голосов
/ 20 сентября 2008

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

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

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

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

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

Ответы [ 16 ]

380 голосов
/ 20 сентября 2008

От Magnus Lycka ответ в списке рассылки :

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

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

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

print('Hello World!', flush=True)
72 голосов
/ 08 октября 2008
# 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

46 голосов
/ 20 сентября 2008

Да, это так.

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

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

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

12 голосов
/ 20 октября 2016

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

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

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

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

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

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

Python 2 не предоставляет аргумент flush, но вы можете эмулировать функцию print типа Python 3, как описано здесь https://stackoverflow.com/a/27991478/3734258.

12 голосов
/ 12 апреля 2014

Следующие работы в 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)
10 голосов
/ 20 сентября 2008

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

7 голосов
/ 01 июля 2015

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

stdbuf -oL python <script>

3 голосов
/ 26 февраля 2017

Возможно переопределить только 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.

...