Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 322 – 역방향 반복

Author:
Raymond Hettinger <python at rcn.com>
Status:
Final
Type:
Standards Track
Created:
24-Sep-2003
Python-Version:
2.4
Post-History:
24-Sep-2003

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

개요

이 제안은 시퀀스에 대한 역방향 반복을 지원하는 내장 함수를 추가하는 것입니다.

동기

인덱싱 가능한 객체의 경우, 역방향 반복을 위한 현재의 접근 방식들은 오류가 발생하기 쉽고, 부자연스러우며, 특별히 가독성이 좋지도 않습니다:

for i in xrange(n-1, -1, -1):
    print seqn[i]

또 다른 현재의 접근 방식은 리스트를 반복하기 전에 뒤집는 것입니다. 이 기법은 컴퓨터 사이클, 메모리, 그리고 코드 줄 수를 낭비합니다:

rseqn = list(seqn)
rseqn.reverse()
for value in rseqn:
    print value

확장 슬라이싱은 코드 오버헤드는 최소화하지만 메모리 효율성, 아름다움, 명료함에는 아무런 도움이 되지 않는 세 번째 접근 방식입니다.

역방향 반복은 정방향 반복보다 훨씬 드물지만, 실제로는 규칙적으로 발생합니다. 아래 Real World Use Cases를 참조하십시오.

제안

__getitem__()과 __len__()을 지원하는 시퀀스 객체에 대해 역방향 이터레이터를 만드는 reversed()라는 내장 함수를 추가합니다.

위의 예제들은 다음과 같이 단순화됩니다:

for i in reversed(xrange(n)):
    print seqn[i]
for elem in reversed(seqn):
    print elem

핵심 아이디어는 역방향 반복을 지정하는 가장 명료하고 오류가 발생하기 어려운 방법은 정방향으로 지정한 다음 reversed라고 말하는 것입니다.

구현은 다음과 같이 간단할 수 있습니다:

def reversed(x):
    if hasattr(x, 'keys'):
        raise ValueError("mappings do not support reverse iteration")
    i = len(x)
    while i > 0:
        i -= 1
        yield x[i]

언어 문법의 변경은 필요하지 않습니다. 이 제안은 완전히 하위 호환성을 갖습니다.

C 구현과 단위 테스트는 다음에 있습니다: https://bugs.python.org/issue834422

BDFL 선언

이 PEP는 Py2.4에 대해 조건부로 수락되었습니다. 이 조건은 해당 함수가 쓸모없는 것으로 밝혀질 경우 Py2.4b1 이전에 제거될 수 있음을 의미합니다.

대안 메서드 이름들

  • reviter – Jeremy Fincher의 제안으로, iter()의 사용 방식과 일치합니다
  • ireverse – itertools의 명명 규칙을 사용합니다
  • inreverse – 저 외에는 아무도 이것을 좋아하지 않는 것 같습니다

reverse라는 이름은 하부 리스트를 변경하는 list.reverse()의 이름과 중복되므로 후보가 아닙니다.

토론

이 PEP 채택에 반대하는 논거는 내장 함수의 수를 적게 유지하고자 하는 바람입니다. 이는 다른 네임스페이스에 숨겨두는 대신 내장 함수로 두었을 때의 단순함과 편의성과 견주어 판단되어야 합니다.

실제 사용 사례

다음은 표준 라이브러리에서 가져온 역방향 반복의 사례들과 왜 역방향 반복이 필요했는지에 대한 설명입니다:

  • atexit.exit_handlers()는 다음을 사용합니다.:
    while _exithandlers:
        func, targs, kargs = _exithandlers.pop()
            . . .
    

    이 애플리케이션에서는 팝(pop) 연산이 필요하므로, 새 함수는 도움이 되지 않을 것입니다.

  • heapq.heapify()는 for i in xrange(n//2 - 1, -1, -1)을 사용하는데, 상위 수준의 순서가 하위 수준의 순서 쌍으로부터 더 쉽게 형성되기 때문입니다. 이 알고리즘의 정방향 버전도 가능하지만, 그렇게 하면 반대 방향으로 기저 리스트를 순회하는 나머지 힙 코드가 복잡해질 것입니다. 대체 코드 for i in reversed(xrange(n//2))는 다루는 범위와 반복 횟수를 명확히 보여줍니다.
  • mhlib.test()는 다음을 사용합니다.:
    testfolders.reverse();
    for t in testfolders:
        do('mh.deletefolder(%s)' % `t`)
    

    역방향 순회가 필요한 이유는 순회 도중 기저 리스트의 끝부분이 변경되기 때문입니다.

  • platform._dist_try_harder()는 for n in range(len(verfiles)-1,-1,-1)을 사용하는데, 이 루프가 verfiles에서 선택된 요소들을 삭제하지만 이후 순회를 위해 나머지 리스트는 그대로 남겨두어야 하기 때문입니다.
  • random.shuffle()은 for i in xrange(len(x)-1, 0, -1)을 사용하는데, 이 알고리즘은 점점 줄어드는 풀에서 요소를 무작위로 선택하는 것으로 이해하는 것이 가장 쉽기 때문입니다. 실제로 이 알고리즘은 정방향으로도 실행할 수 있지만, 직관성이 떨어져 문헌에서 그런 방식으로 제시되는 경우는 드뭅니다. 대체 코드 for i in reversed(xrange(1, len(x)))는 시각적으로 검증하기가 훨씬 쉽습니다.
  • rfc822.Message.__delitem__()는 다음을 사용합니다.:
    list.reverse()
    for i in list:
        del self.headers[i]
    

    역방향 순회가 필요한 이유는 순회 도중 기저 리스트의 끝부분이 변경되기 때문입니다.

거부된 대안

이터러블을 끝까지 실행하여 결과를 저장한 다음, 그 결과에 대한 역방향 이터레이터를 반환하는 방식으로 reversed()를 모든 이터러블에 적용하려 한 여러 변형이 제출되었습니다. 완전한 일반성이라는 일부 개념은 충족시키지만, 입력을 끝까지 실행하는 것은 애초에 이터레이터를 사용하는 목적에 반합니다. 또한 기저 이터레이터가 무한하다면 작은 재앙이 뒤따릅니다.

이 함수를 다른 모듈에 넣거나 타입 객체에 부착하는 방안은 고려되지 않고 있습니다. 사촌격인 zip()enumerate()와 마찬가지로, 이 함수는 일상적인 프로그래밍에서 직접 접근 가능해야 합니다. 각각은 동기 반복, 루프 카운팅, 역방향 반복이라는 기본적인 루프 문제를 해결합니다. 점(dot) 표기 형태의 접근을 요구한다면 이들의 단순성, 일상적 유용성, 접근성을 해칠 것입니다. 이들은 특정 응용 영역에 국한되지 않는 핵심적인 루프 구성 요소입니다.