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

Python 개선 제안 한국어 번역

PEP 284 – 정수 for 루프

Author:
David Eppstein <eppstein at ics.uci.edu>, Gregory Ewing <greg.ewing at canterbury.ac.nz>
Status:
Rejected
Type:
Standards Track
Created:
01-Mar-2002
Python-Version:
2.3
Post-History:


Table of Contents

번역·라이선스 안내

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

초록

이 PEP는 “for” 키워드 뒤에 올 수 있는 표현식의 범위를 확장하여 다음과 같은 3방향 비교를 허용함으로써 정수 구간에 대한 반복을 단순화할 것을 제안합니다.

for lower <= var < upper:

이는 현재의

for item in list:

문법. 그 결과로 만들어지는 루프나 리스트 반복은 주어진 구간의 왼쪽 끝점에서 시작하여, 비교를 참으로 만드는 var의 모든 값을 순회합니다.

결정

이 PEP는 거부되었습니다. 이 제안에는 고칠 수 있는 문제가 여럿 있었습니다(2005년 6월 18일 Raymond Hettinger가 python-dev에 올린 글에 나열된 수정 사항을 참고하십시오 [1]). 그러나 그러한 수정을 거친 후에도 이 제안은 지지를 얻지 못했습니다. 구체적으로 Guido는 range() 형식을 고쳐야 한다는 전제를 받아들이지 않았습니다. “(15년 전) range()의 핵심은 숫자에 대한 루프를 지정하는 문법이 필요 없게 하는 것이었습니다. 저는 그것이 잘 작동해 왔고 고쳐야 할 것은 없다고 생각합니다(range()가 이터레이터가 되어야 한다는 점은 예외인데, 이는 Python 3.0에서 이루어질 것입니다).”

근거

Python에서 for 루프가 가장 흔하게 쓰이는 용도 중 하나는 정수 구간을 순회하는 것입니다. Python은 그러한 구간에 대한 리스트와 이터레이터를 생성하는 range()와 xrange() 함수를 제공하며, 이 함수들은 가장 빈번한 경우인 0부터 증가하는 반열린 구간에 가장 잘 맞습니다. 그러나 range() 문법은 열린 구간이나 닫힌 구간에는 더 어색하며, 반복 순서를 뒤집을 때 대칭성이 부족합니다. 또한 익숙하지 않은 함수를 호출하기 때문에 Python을 처음 접하는 사람은 range()나 xrange()를 사용하는 코드를 이해하기 어렵습니다.

자연스럽고 직관적인 정수 반복 문법이 없다는 인식 때문에 python-list에서 열띤 논쟁이 벌어졌고, 이 PEP 이전에도 최소 네 개의 PEP가 만들어졌습니다. PEP 204 (거부됨)는 정수 범위에 Python의 슬라이스 문법을 재사용할 것을 제안하여 더 간결한 문법을 얻었지만, 다중 인자 range()의 가독성 문제는 해결하지 못했습니다. PEP 212 (보류됨)는 리스트를 정수 인덱스의 시퀀스로 직접 변환하는 여러 문법을 제안했는데, 이는 이러한 변환에 쓰이는 현재의 다음 관용구를 대체하기 위한 것이었습니다.

range(len(list))

그리고 PEP 281은 같은 관용구를 다음과 같이 쓸 수 있게 하여 단순화할 것을 제안합니다.

range(list).

PEP 276은 정수를 이터레이터로 자동 변환할 수 있게 하자고 제안하는데, 이는 가장 흔한 반열린 구간의 경우를 단순화하지만 다른 유형의 구간이 지닌 복잡성은 다루지 않습니다. python-list에서는 그 밖의 대안들도 논의되었습니다.

여기서 설명하는 해결책은 for 루프와 리스트 컴프리헨션 두 맥락 모두에서 “for” 키워드 뒤에 3방향 비교를 허용하는 것입니다.:

for lower <= var < upper:

이렇게 하면 비교의 왼쪽 경계에서 시작하여 오른쪽 경계에서 끝나는, 연속된 정수 구간에 대한 반복이 이루어집니다. 사용된 비교 연산의 정확한 종류에 따라 구간의 각 끝이 열려 있는지 닫혀 있는지, 그리고 정수를 오름차순으로 다룰지 내림차순으로 다룰지가 결정됩니다.

이 문법은 표준 수학 표기법과 매우 비슷하므로, Python 초보자에게는 현재의 range() 문법보다 더 익숙할 가능성이 높습니다. 열린 구간과 닫힌 구간의 끝점을 똑같이 쉽게 표현할 수 있으며, 정수 구간을 뒤집으려면 두 끝점을 맞바꾸고 비교 방향을 반대로 하기만 하면 됩니다. 또한 이러한 루프의 의미는 기존 Python for 루프를 해석하는 한 가지 방식과 매우 유사합니다.:

for item in list

위 루프는 item의 값 중에서 다음 표현식을

item in list

참으로 만드는 값만을 정확히 순회합니다. 마찬가지로, 다음과 같은 새로운 형식은

for lower <= var < upper:

var의 정수 값 중에서 다음 표현식을

lower <= var < upper

참으로 만드는 값만을 정확히 순회할 것입니다.

명세

현재 다음과 같은 for 문의 문법을

for_stmt: "for" target_list "in" expression_list ":" suite
      ["else" ":" suite]

아래에 설명한 대로 확장할 것을 제안합니다:

for_stmt: "for" for_test ":" suite ["else" ":" suite]
for_test: target_list "in" expression_list |
        or_expr less_comp or_expr less_comp or_expr |
        or_expr greater_comp or_expr greater_comp or_expr
less_comp: "<" | "<="
greater_comp: ">" | ">="

마찬가지로, 현재 다음과 같은 리스트 컴프리헨션의 문법을

list_for: "for" expression_list "in" testlist [list_iter]

다음으로 대체하여 확장할 것을 제안합니다:

list_for: "for" for_test [list_iter]

모든 경우에 for_test로 이루어진 표현식은 표현식 내 비교와 동일한 우선순위 규칙을 따릅니다. for_test 안의 두 comp_operator는 반드시 서로 비슷한 종류여야 하며, 이는 그러한 제약이 없는 표현식 내 연쇄 비교와 다른 점입니다.

for 루프 문법의 왼쪽과 오른쪽에 나타나는 두 or_expr을 루프의 경계라고 부르고, 가운데의 or_expr을 루프의 변수라고 부릅니다. 새 문법을 사용하는 for 루프가 실행되면 두 경계의 표현식이 평가되고, 사용된 비교 연산에 따라 두 경계 사이의 모든 정수를 순회하는 이터레이터 객체가 생성됩니다. 이터레이터는 왼쪽 경계와 같거나 가까운 정수에서 시작하며, 비교 연산이 less_comp로 기술된 집합에 속하면 +1, greater_comp로 기술된 집합에 속하면 -1의 간격으로 나머지 정수를 차례로 거쳐 갑니다. 그런 다음 실행은 마치 표현식이 다음과 같았던 것처럼 진행됩니다

for variable in iterator

여기서 “variable”은 루프의 변수를, “iterator”는 주어진 정수 구간에 대해 생성된 이터레이터를 가리킵니다.

정수 for 루프에서 루프 변수가 가지는 값은 경계의 크기에 따라 일반 정수일 수도 있고 long 정수일 수도 있습니다. 정수 for 루프의 두 경계는 모두 실수 숫자 타입(integer, long, float)으로 평가되어야 합니다. 그 밖의 값이면 for 루프 문이 TypeError 예외를 발생시킵니다.

문제점

Python 메일링 리스트에서 이 제안과 관련 제안들을 논의하는 과정에서 다음과 같은 논점이 제기되었습니다.

  • 오른쪽 경계는 한 번만 평가해야 합니까, 아니면 루프를 돌 때마다 평가해야 합니까? 왼쪽 경계는 분명히 한 번만 평가하는 것이 타당합니다. 일관성과 효율성을 위해 오른쪽 경계에도 같은 규칙을 적용하기로 했습니다.
  • 새 문법은 정수 for 루프를 상당히 단순하게 만들어 주지만, 새 문법을 사용하는 리스트 컴프리헨션은 그만큼 단순하지 않습니다. for 루프가 컴프리헨션보다 더 자주 쓰이므로 이는 적절하다고 생각합니다.
  • 이 제안으로는 xrange가 만들어 내는 것과 같은 정수 이터레이터 객체에 접근할 수 없습니다. 맞는 말이지만, 이는 일반적인 리스트 컴프리헨션 문법의 결함으로서 이 제안의 범위를 벗어난다고 봅니다. 게다가 xrange()는 여전히 사용할 수 있습니다.
  • 이 제안은 1과 -1 이외의 증분을 허용하지 않습니다. 보다 일반적인 산술 수열은 range()나 xrange(), 또는 다음과 같은 리스트 컴프리헨션 문법으로 만들어야 합니다.
    [2*x for 0 <= x <= 100]
    
  • 삼항 비교의 가운데에 놓인 루프 변수의 위치는 현재의
    for item in list
    

    문법에서의 변수만큼 눈에 잘 띄지 않으므로 가독성이 떨어질 수 있습니다. 자연스러운 정수 반복 문법으로 얻는 가독성 향상이 이러한 손실보다 더 크다고 생각합니다.

  • 이 PEP는 어느 정도 PEP 276과 같은 문제를 다룹니다. PEP 276은 주로 0에서 시작하는 반열린 구간(range()의 쉬운 경우)을 다루고 이 PEP는 주로 그 밖의 모든 경우를 단순화하는 데 초점을 두므로, 저희는 두 PEP가 서로 충돌하지 않는다고 생각합니다. 그러나 이 PEP가 승인된다면, 정수 루프를 위한 새롭고 더 단순한 문법이 PEP 276의 동기를 어느 정도 약화할 수 있습니다.
  • 정수 루프에 부동소수점 경계를 허용하는 것이 타당한지는 분명하지 않습니다. float가 부정확한 값을 나타낸다면, 어떻게 그것으로 정확한 정수 시퀀스를 결정할 수 있겠습니까? 반면에 float 경계를 허용하지 않으면, 지금 range()와 함께 사용하기 어려운 것처럼 정수 for 루프에서도 floor()와 ceiling()을 사용하기 어려워집니다. 저희는 유연성 쪽을 택했지만, 이로 인해 주어진 비교를 참으로 만드는 가장 작은 정수 값과 가장 큰 정수 값을 결정하는 데 구현상의 어려움이 생길 수 있습니다.
  • int, long, float 이외의 타입도 경계로 허용해야 합니까? 또 다른 방법은 모든 경계를 int()로 정수로 변환하고, float뿐 아니라 그렇게 변환할 수 있는 것이면 무엇이든 경계로 허용하는 것입니다. 그러나 이렇게 하면 의미가 바뀝니다. 0.3 <= x는 int(0.3) <= x와 같지 않으며, 하한이 0.3인 루프가 0에서 시작한다면 혼란스러울 것입니다. 또한 일반적으로 int(f)는 f와 크게 차이 날 수 있습니다.

구현

현재 사용할 수 있는 구현은 없습니다. 구현에 큰 어려움은 없을 것으로 예상합니다. 필요하다면 각 “for” 키워드 뒤의 일반 표현식을 파싱하고, 그 표현식의 최상위 연산이 “in”인지 삼중 비교인지 검사하여 새 문법을 인식할 수 있습니다. Python 컴파일러는 새 문법이 쓰인 모든 곳을 특수한 이터레이터 객체의 항목들에 대한 루프로 변환하게 됩니다.

참고 문헌