출력 버퍼링 비활성화


532

파이썬의 인터프리터에서 출력 버퍼링이 기본적으로 활성화되어 sys.stdout있습니까?

답변이 긍정적이면 어떻게 비활성화 할 수 있습니까?

지금까지 제안 :

  1. 사용 -u명령 줄 스위치를
  2. sys.stdout모든 쓰기 후에 플러시되는 객체를 감싸십시오 .
  3. PYTHONUNBUFFERED환경 변수 설정
  4. sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

일부 전역 플래그를 설정하는 다른 방법이 있나요 sys/ sys.stdout실행 중에 프로그램은?


7
Python 3의`print '에 대해서는 this answer을 참조하십시오 .
Antti Haapala

1
단점은 -u컴파일 된 바이트 코드 또는 __main__.py파일을 진입 점으로 사용하는 앱에서는 작동하지 않는다는 것입니다 .
akhan

전체 CPython 초기화 로직은 다음과 같습니다. github.com/python/cpython/blob/v3.8.2/Python/…
Beni Cherniavsky-Paskin

답변:


443

에서 메일 링리스트에 매그너스 Lycka 답변 :

"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'

71
원본 sys.stdout은 여전히 ​​sys .__ stdout__로 사용 가능합니다. 그냥 당신이 그것을 필요로 =)
Antti Rasinen

40
#!/usr/bin/env python -u작동하지 않습니다 !! 여기를
wim

6
__getattr__상속을 피하기 위해?!
Vladimir Keleshev

32
두통을 피하기위한 참고 사항 : 내가 알았 듯이 출력 버퍼링은 출력이 tty 또는 다른 프로세스 / 파이프로 이동하는지에 따라 다르게 작동합니다. tty로 이동하면 각 \ n 후에 플러시 되지만 파이프에서는 버퍼링됩니다. 후자의 경우 이러한 세척 솔루션을 사용할 수 있습니다. Cpython에서 (pypy가 아닌 !!!) : sys.stdin에서 for for input 을 반복하면 ... for 루프는 루프 본문이 실행되기 전에 여러 줄을 수집합니다. 이것은 일괄 처리이지만 버퍼링처럼 동작합니다. 대신 true를 유지하십시오. line = sys.stdin.readline ()
tzp

5
@tzp : 루프 iter()대신 사용할 수 있습니다 while: for line in iter(pipe.readline, ''):. 파이썬 3에서는 for line in pipe:최대한 빨리 생산할 필요가 없습니다 .
jfs


77
# 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", 파이썬 메일 링리스트 어딘가에.


Python3에서는 플러시 함수로 인쇄 함수의 이름을 무시할 수 있습니다. 그래도 더러운 속임수입니다!
meawoppl

16
@meawoppl : Python 3.3부터 flush=True매개 변수를 print()함수에 전달할 수 있습니다.
jfs

최근 버전의 Python에서는 응답을 표시하기위한 응답 편집이 유효하지 않습니다
Mike

둘 다 os.fdopen(sys.stdout.fileno(), 'wb', 0)( b바이너리에 주의하십시오 ) flush=True3.6.4에서 저를 위해 일하십시오. 그러나 하위 프로세스 를 사용하여 다른 스크립트를 시작하는 python3경우 여러 개의 Python 인스턴스가 설치된 경우을 지정했는지 확인하십시오 .
not2qubit

1
@ not2qubit : 사용 os.fdopen(sys.stdout.fileno(), 'wb', 0)하면 TextIO스트림이 아닌 이진 파일 객체로 끝납니다 . TextIOWrapper믹스 에 a를 추가 해야합니다 ( write_through모든 버퍼를 제거하거나 line_buffering=True개행 만 플러시 하는 데 사용 ).
Martijn Pieters

55

그렇습니다.

"-u"스위치를 사용하여 명령 줄에서 비활성화 할 수 있습니다.

또는 모든 쓰기마다 sys.stdout에서 .flush ()를 호출하거나 자동으로 수행하는 객체로 래핑 할 수 있습니다


19

이것은 Cristóvão D. Sousa의 답변과 관련이 있지만 아직 언급 할 수 없습니다.

버퍼링되지 않은 출력 을 항상 유지 하기 위해 Python 3flush키워드 인수 를 사용하는 간단한 방법 은 다음과 같습니다.

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

그 후 인쇄는 항상 출력을 직접 플러시합니다 (제외됨 flush=False).

(a) 이것은 모든 출력을 리디렉션하지 않으므로 질문에 부분적으로 만 대답한다는 점에 유의하십시오. 그러나 파이썬에서 / 로 print출력을 만드는 가장 일반적인 방법 이라고 생각 합니다. 이 두 줄은 아마도 대부분의 사용 사례를 다룹니다.stdoutstderr

참고 (b)는 정의한 모듈 / 스크립트에서만 작동합니다. 모듈을 작성할 때 좋을 수 있습니다 sys.stdout.

Python 2flush인수를 제공하지 않지만 https://stackoverflow.com/a/27991478/3734258에print 설명 된대로 Python 3 유형 함수를 에뮬레이션 할 수 있습니다 .


1
flushpython2에 kwarg 가 없다는 것을 제외하고 .
o11c

@ o11c, 그렇습니다. 나는 (내가 그것을 테스트하지만, 어떻게 든 내가 겉으로는 혼란스러워했다 확신했다 : 나는 지금의 좋은 희망 덕분에, 내 대답을 수정했습니다.!

14
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 ()이 dem 등성이 아니며 여러 호출로 인해 다음과 같은 오류가 발생합니다.

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에 추가하는 것은 순환 할 수없는주기가있는 위치에 있으므로 좋은 아이디어가 아닙니다.이를 확인하고 싶을 수도 있습니다.


2
일부 사람들이 제안한대로 노인이 stdout여전히 살아 있다면 sys.__stdout__쓰레기는 필요하지 않을 것입니다. 그래도 멋진 트릭입니다.
Thomas Ahle

1
@Federico의 답변과 마찬가지로 Python 3에서는 ValueError: can't have unbuffered text I/O호출 할 때 예외가 발생하므로 Python 3에서는 작동하지 않습니다 print().
gbmhunter

"또 다른 가능성"은 처음에는 가장 강력한 솔루션처럼 보이지만 불행히도 다른 스레드가 sys.stdout.close () 이후와 os.dup2 (temp_fd, fileno 전에 open ()을 호출하는 경우 경쟁 조건이 발생합니다 ). ThreadSanitizer에서 귀하의 기술을 사용해 보았을 때 이것을 발견했습니다. dup2 ()가 open ()과 경쟁 할 때 EBUSY에서 실패한다는 사실로 인해 실패가 더 커집니다. 참조 stackoverflow.com/questions/23440216/...
돈 해치

13

다음은 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)

그것을 두 번 실행하면 Windows에서 충돌합니다 :-)
Michael Clerx

@MichaelClerx 흠 흠, 항상 파일 xD를 닫는 것을 잊지 마십시오.

Raspbian 9 파이썬 3.5이 날을 제공 OSError: [Errno 29] Illegal seek하는 라인에sys.stdout = os.fdopen(sys.stdout.fileno(), 'a+', buf_arg)
sdbbs

12

예, 기본적으로 활성화되어 있습니다. python을 호출 할 때 명령 행에서 -u 옵션을 사용하여이를 비활성화 할 수 있습니다.


7

stdbuf 유틸리티를 사용 하여 Python을 실행할 수도 있습니다 .

stdbuf -oL python <script>


2
라인 버퍼링 ( -oL활성화로)은 여전히 ​​버퍼링입니다. f / e stackoverflow.com/questions/58416853/…를 참조 end=''하여 출력이 더 이상 즉시 표시되지 않는 이유 를 묻습니다 .
Charles Duffy

사실이지만 라인 버퍼링이 기본값 (tty)이므로 출력이 완전히 버퍼링되지 않는다고 가정하면 코드를 작성하는 것이 합리적 print(..., end='', flush=True)입니까? OTOH는 여러 프로그램이 동시에 동일한 출력에 쓰면 트레이드 오프는 즉각적인 진전을 보는 것에서 출력 믹스 업을 줄이는 것으로 바뀌는 경향이 있으며 라인 버퍼링이 매력적입니다. 그렇다면 외부에서 명시 적 및 제어 버퍼링을 작성하지 않는 것이 좋습니다 flush.
Beni Cherniavsky-Paskin

내 생각 엔 프로세스 자체는 언제, 왜 호출해야하는지 결정해야합니다 flush. 외부 버퍼링 제어는이 문제를 해결해야합니다.
dyomas

7

Python 3에서는 항상 flush = True를 보내도록 print 함수를 원숭이 패치 할 수 있습니다.

_orig_print = print

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

주석에서 지적했듯이 다음을 통해 flush 매개 변수를 값에 바인딩하여 이것을 단순화 할 수 있습니다 functools.partial.

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

3
궁금한 점이 있지만, 이것이 완벽한 유스 케이스가 functools.partial아닐까요?
0xC0000022L 2016 년

@ 0xC0000022L 덕분에 더 좋아 보입니다! print = functools.partial(print, flush=True)나를 위해 잘 작동합니다.
MarSoft

0xC0000022L 실제로, 나는 그 지적에 감사하는 옵션을 보여 포스트를 업데이트 @
올리버

3
모든 곳에 적용하려면import builtins; builtins.print = partial(print, flush=True)
Perkins

4

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)

1
Windows에 상응하는 창이 있습니다 : stackoverflow.com/questions/881696/…
Tobu

12
O_SYNC는이 질문이 요구하는 사용자 공간 수준 버퍼링과 전혀 관련이 없습니다.
apenwarr

4

오버라이드 만 가능 writesys.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.


3

버퍼되지 않은 파일을 만들고이 파일을 sys.stdout에 할당 할 수 있습니다.

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

시스템 제공 stdout을 마술처럼 변경할 수는 없습니다. OS가 파이썬 프로그램에 제공하기 때문에.


3

충돌없이 작동하는 변형 (적어도 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
sys.stdout is sys.__stdout__이름 속성이있는 대체 객체에 의존하지 않고 확인해야합니까 ?
leewz

gunicorn이 어떤 이유로 PYTHONUNBUFFERED를 존중하지 않는 경우에 효과적입니다.
Brian Arsuaga

3

(나는 의견을 게시했지만 어떻게 든 잃어 버렸습니다. 그래서 다시 :)

  1. 내가 알았 듯이 CPython (적어도 Linux에서는)은 출력 위치에 따라 다르게 작동합니다. tty로 이동하면 각 출력 후에 출력이 플러시 \n'
    됩니다. 파이프 / 프로세스로 이동하면 버퍼링되어 사용할 수 있습니다.flush() 위에서 권장 된 기반 솔루션 또는 -u 옵션을 .

  2. 출력 버퍼링과 약간 관련이 있습니다.
    입력의 라인을 반복하는 경우

    for line in sys.stdin:
    ...

CPython 에서 for 구현은 잠시 동안 입력을 수집 한 다음 많은 입력 행에 대해 루프 본문을 실행합니다. 스크립트가 각 입력 행에 대한 출력을 쓰려고하면 출력 버퍼링처럼 보이지만 실제로는 일괄 처리이므로 , 등의 기술 중 어느 것도 도움이 되지 않습니다 . 흥미롭게도 pypy 에서는 이러한 동작이 없습니다 . 이것을 피하기 위해flush()

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


귀하의 의견은 다음과 같습니다 . 이전 Python 버전의 버그 일 수 있습니다. 예제 코드를 제공해 주시겠습니까? for line in sys.stdinvs.for line in iter(sys.stdin.readline, "")
jfs

sys.stdin의 줄 : print ( "Line :"+ line); sys.stdout.flush ()
tzp

미리 읽기 버그 처럼 보입니다 . 파이썬 2와 stdin이 파이프 인 경우에만 발생합니다. 이전 주석의 코드는 문제를 보여줍니다 ( for line in sys.stdin지연된 응답 제공)
jfs

2

버퍼되지 않은 출력을 얻는 한 가지 방법은 sys.stderr대신 쓰기를 명시 적으로 강제하기 위해 sys.stdout또는 대신 호출 sys.stdout.flush()하는 것입니다.

다음을 수행하여 인쇄 된 모든 내용을 쉽게 리디렉션 할 수 있습니다.

import sys; sys.stdout = sys.stderr
print "Hello World!"

또는 특정 print문장으로 만 리디렉션하려면 다음을 수행하십시오.

print >>sys.stderr, "Hello World!"

stdout을 재설정하려면 다음을 수행하십시오.

sys.stdout = sys.__stdout__

1
나중에 표준 리디렉션을 사용하여 출력을 캡처하려고 할 때 아무것도 캡처하지 않을 때 매우 혼란 스러울 수 있습니다! 추신 당신의 stdout 대담 하 고 물건입니다.
freespace

1
stderr에 선택적으로 인쇄 할 때주의해야 할 한 가지 사항은 이로 인해 라인이 제대로 표시되지 않기 때문에 타임 스탬프가 없으면 혼동 될 수 있습니다.
haridsv
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.