Experiments never fail s2

실험에 실패란 없다 블로그 시즌 2

파이썬 표준입력(sys.stdin) 다룰 때 주의할 점

Pythonist - 2013년 5월 16일 1:20:02 오전

필요상 파이썬 스크립트를 두개로 쪼개고 파이프(pipe)로 연결해서 돌렸다. 이렇게 하면 좀 더 효율적으로 돌아갈거라 기대했다. 앞엣 스크립트가 끝나지 않아도 결과가 나오는대로 뒷 스크립트로 들어가서 좀 더 빠르게 돌겠지. 그런데 왠걸, 앞 스크립트가 끝날 때 까지 기다린다는. 이래서는 파이프로 쓰는 이유가 없지 하고 좀 자세히 알아봤다.

간단한 예제파일을 만들어봤다. a.py 가 다음과 같고,

import sys
import time
for line in sys.stdin:
    time.sleep(2)
    sys.stdout.write(line.upper())
    sys.stdout.flush()

b.py 가 다음과 같을 때,

import sys
for line in sys.stdin:
    sys.stdout.write(line.capitalize())
    sys.stdout.flush()

다음처럼 실행하면,

$ python a.py < test.txt 
HELLO
WORLD
PYTHON

윗 결과가 2초마다 한줄씩 출력된다. 근데, 이를 b.py 에 연결하면,

$ python a.py < test.txt | python b.py
Hello
World
Python

위 결과가 6초후에 한번에 출력된다. 이러면 안되잖아. 얘도 2초에 한줄씩 출력해야지.

이것저것 키워드를 만들어서 검색해봤는데 마땅한 결과를 찾지 못하고는 스택오버플로우에 질문 올리고는 트윗. 적당한 용어와 영작이 아닌 듯 하여 좀 걱정되긴 하지만.

그랬더니, 홍민희님 바로 답변.

그러고는, 재야의 숨은 고수 falsetru님 이미 다 알고 있다는 듯,

파이썬 질문은 스택오버플로우보다 falsetru님한테 바로 물어보는게 더 빨라 보임.

"man python" 하면 다음처럼 나온다.

       -u     Force  stdin,  stdout  and  stderr to be totally unbuffered.  On
              systems where it matters, also put stdin, stdout and  stderr  in
              binary  mode.   Note  that there is internal buffering in xread‐
              lines(), readlines() and file-object  iterators  ("for  line  in
              sys.stdin")  which  is  not  influenced by this option.  To work
              around this, you will want to use "sys.stdin.readline()"  inside
              a "while 1:" loop.

"for line in sys.stdin" 구문은 생각처럼 그때그때 결과를 바로 뱉어내지 않고 파일의 끝(EOF, End Of File) 까지 기다리나보다. 따라서 기대한 것 처럼 결과를 얻으려면 for 문 대신 while 문과 sys.stdin.readline()을 써야 한다고.

즉 b.py 를

import sys
while True:
    line = sys.stdin.readline()
    if not line:
        break
    sys.stdout.write(line.capitalize())
    sys.stdout.flush()

위처럼 for문 대신 while문으로 써야 한다. 내가 알고 있는 반복자 for 문은 이렇게 돌면 안되는데. 표준입력처리엔 다른 뭔가가 있나보다.

이윽고, 스택오버플로우에도 답변이 달렸다. while 문을 써야 한다고. 그래도 표준입력 반복자가 for 문에서 EOF까지 기다린다는 건 아무리 생각해도 불만이다. 이것 저것 찾다보니, 파이썬 3에서는 안그런다고. 오~ 실제 테스트해보니 파이썬 3에서는 for문도 바로바로 결과가 나온다.

"man python3" 하면,

       -u     Force  the  binary I/O layers of stdin, stdout and stderr to be unbuffered.
              The text I/O layer will still be line-buffered.

이렇게만 나온다. stdin EOF 문제는 정리된 듯. 파이썬 3 을 써야하는 또 다른 이유!

P.S.

몇몇분의 조언. 파이썬 2.x 의 "for line in stdin" 이 EOF까지 기다리는 건 아니라고 함. 버퍼링할뿐. text.txt 를 크게 만들고 sleep(0.1) 해서 돌려보니 내 맥북에서 약 800라인씩 버퍼링됨. 이정도면 그냥 무시하고 써도 될 수준인 것 같다. 내가 처음 이 문제를 발견했을 때는 버퍼링도 안하는 것 같았는데... 좀 더 면밀한 테스트 후 포스팅했어야.


Posted by Hyungyong Kim (yong27)

태그(들): EOF, python, python3, stdin


comments powered by Disqus

« 캘리포니아주 1번국도 드라이브

요세미티 국립공원 하루 투어 »