파이썬에서 파일을 한 줄씩 읽어야합니까?


137

선사 시대 (Python 1.4)에서 우리는 다음을 수행했습니다.

fp = open('filename.txt')
while 1:
    line = fp.readline()
    if not line:
        break
    print line

파이썬 2.1 이후, 우리는 :

for line in open('filename.txt').xreadlines():
    print line

파이썬 2.3에서 편리한 반복자 프로토콜을 얻기 전에 다음과 같이 할 수 있습니다.

for line in open('filename.txt'):
    print line

좀 더 장황한 예제를 보았습니다.

with open('filename.txt') as fp:
    for line in fp:
        print line

이것이 선호되는 방법입니까?

[edit] with 문으로 파일을 닫을 수 있지만 파일 객체의 반복자 프로토콜에 포함되지 않은 이유는 무엇입니까?


4
imho, 마지막 제안은 이전의 제안보다 더 장황하지 않습니다. 더 많은 작업을 수행합니다 (완료되면 파일이 닫히도록 보장).
azhrei

1
@ azhrei 한 줄 이상이므로 객관적으로 더 장황합니다.
thebjorn

7
나는 당신이 말하는 것을 얻지 만 사과와 사과를 비교한다고 말하고 있습니다. 게시물에서 두 번째 마지막 제안에는 마지막 옵션의 기능과 일치하는 예외 처리 코드가 필요합니다. 실제로는 더 장황합니다. 마지막 두 옵션 중 가장 좋은 것이 컨텍스트에 달려 있다고 생각합니다.
azhrei

답변:


227

다음이 선호되는 이유는 정확히 한 가지입니다.

with open('filename.txt') as fp:
    for line in fp:
        print line

우리는 모두 가비지 콜렉션에 대한 CPython의 비교적 결정적인 참조 계산 체계에 의해 망쳐졌습니다. 다른 가설적인 파이썬의 구현은 with다른 스키마를 사용하여 메모리를 되 찾을 경우 반드시 블록 없이 파일을 "빠르게"닫을 필요는 없습니다 .

이러한 구현에서 가비지 수집기가 분리 된 파일 핸들에서 종료자를 호출하는 것보다 코드가 파일을 빠르게 여는 경우 OS에서 "너무 많은 파일이 열렸습니다"오류가 발생할 수 있습니다. 일반적인 해결 방법은 GC를 즉시 트리거하는 것이지만 이는 해킹에 지나지 않으며 라이브러리의 오류를 포함하여 오류가 발생할 수있는 모든 함수에 의해 수행되어야합니다 . 악몽이야

아니면 그냥 with블록을 사용할 수도 있습니다 .

보너스 질문

(문제의 객관적인 측면에만 관심이있는 경우 지금 읽기를 중지하십시오.)

파일 객체의 반복자 프로토콜에 왜 포함되어 있지 않습니까?

이것은 API 디자인에 대한 주관적인 질문이므로 두 부분으로 주관적인 답변이 있습니다.

창자 수준에 그 반복자 프로토콜을 만들기 때문에, 이것은 잘못된 느낌 할 별도의 두 가지-으로 반복 라인 이상 파일 가까운 핸들과 간단한 보이는 함수는 두 개의 작업을 할 수 있도록하는 것이 좋은 생각입니다. 이 경우 반복자는 파일의 내용과 유사하게 기능적인 값 기반 방식으로 관련되어 있기 때문에 특히 나쁘지만 파일 핸들 관리는 완전히 별개의 작업입니다. 하나의 행동으로 보이지 않게 두 가지를 모두 분류하는 것은 코드를 읽는 사람에게는 놀라운 일이며 프로그램 행동에 대한 추론을 어렵게 만듭니다.

다른 언어들도 본질적으로 같은 결론을 내 렸습니다. Haskell은 이른바 "게으른 IO"로 잠깐 동안 파일을 반복하여 스트림이 끝날 때 자동으로 닫히도록하지만 최근에는 Haskell에서 게으른 IO를 사용하지 않는 것이 일반적으로 바람직하지 않습니다. 사용자는 주로 Conduit과 같은보다 명시적인 리소스 관리로 이동하여 withPython 의 블록 과 유사하게 작동 합니다.

기술적 인 수준에서 파이썬에서 파일 핸들로 할 수있는 일이있을 수 있습니다. 반복은 파일 핸들을 닫으면 잘 작동하지 않습니다. 예를 들어 파일을 두 번 반복해야한다고 가정합니다.

with open('filename.txt') as fp:
    for line in fp:
        ...
    fp.seek(0)
    for line in fp:
        ...

이것은 일반적인 사용 사례는 아니지만, 맨 처음 세 줄을 가진 기존 코드베이스에 맨 아래에 세 줄의 코드를 추가했을 수도 있습니다. 반복이 파일을 닫으면 그렇게 할 수 없습니다. 따라서 반복과 리소스 관리를 별도로 유지하면 코드 덩어리를 더 크고 작동하는 Python 프로그램으로 쉽게 작성할 수 있습니다.

조합 성은 언어 또는 API의 가장 중요한 유용성 기능 중 하나입니다.


1
그것은 op에 대한 내 의견에 "언제"를 설명하기 때문에 +1 ;-)
azhrei

대체 구현에서도 with 핸들러는 수백 개의 파일을 매우 빠르게 연속적으로 여는 프로그램에 대해서만 문제를 일으킬 것입니다. 대부분의 프로그램은 문제가없는 댕글 링 파일 참조로 얻을 수 있습니다. 비활성화하지 않으면 결국 GC가 언젠가 시작하여 파일 핸들을 정리합니다. with그래도 마음의 평화를 주므로 여전히 모범 사례입니다.
Lie Ryan

1
@ DietrichEpp : 아마도 "매달린 파일 참조"는 올바른 단어가 아니 었습니다. 실제로 더 이상 액세스 할 수 없지만 아직 닫히지 않은 파일 핸들을 의미했습니다. 어쨌든 GC는 파일 객체를 수집 할 때 파일 핸들을 닫습니다. 따라서 파일 객체에 대한 추가 참조가없고 GC를 비활성화하지 않고 많은 파일을 빠르게 열지 않는 한 연속해서 파일을 닫지 않아서 "너무 많은 파일을 열지"않을 것입니다.
Lie Ryan

1
예, "가비지 수집기가 분리 된 파일 핸들에서 종료자를 호출하는 것보다 코드가 파일을 빠르게 여는 경우"라는 의미입니다.
Dietrich Epp

1
함께 사용해야하는 더 큰 이유는 파일을 닫지 않으면 반드시 즉시 쓰지 않아도되기 때문입니다.
안티몬

20

예,

with open('filename.txt') as fp:
    for line in fp:
        print line

갈 길입니다.

더 장황하지 않습니다. 더 안전합니다.


5

여분의 줄로 꺼져 있다면 다음과 같이 래퍼 기능을 사용할 수 있습니다.

def with_iter(iterable):
    with iterable as iter:
        for item in iter:
            yield item

for line in with_iter(open('...')):
    ...

파이썬 3.3 yield from에서이 문장은 이것을 더 짧게 만듭니다 :

def with_iter(iterable):
    with iterable as iter:
        yield from iter

2
xreadlines .. 함수를 호출하고 xreadlines.py라는 파일에 넣으면 파이썬 2.1 구문으로 돌아갑니다. :-)
thebjorn

@thebjorn : 아마도, 그러나 당신이 인용 한 Python 2.1 예제는 대체 구현에서 닫히지 않은 파일 핸들러로부터 안전하지 않았습니다. 닫히지 않은 파일 핸들러로부터 안전한 Python 2.1 파일 읽기에는 최소 5 줄이 필요합니다.
Lie Ryan

-1
f = open('test.txt','r')
for line in f.xreadlines():
    print line
f.close()

5
이 질문에 실제로 대답하지 않습니다
Thayne
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.