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()와 마찬가지로, 이 함수는 일상적인 프로그래밍에서 직접 접근할 수 있어야 합니다. 각각은 기본적인 루프 문제를 해결합니다: 잠금 단계(lock-step) 반복, 루프 횟수 세기, 그리고 역방향 반복입니다. 어떤 형태로든 점 표기 접근을 요구하면 이들의 단순성, 일상적 유용성, 접근성이 저해됩니다. 이들은 특정 응용 분야에 종속되지 않는 핵심 루프 구성 요소입니다.