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:
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
개요
이 PEP는 “for” 키워드 뒤에 허용되는 표현식의 범위를 확장하여 다음과 같은 삼중 비교를 허용함으로써 정수 구간에 대한 반복을 단순화할 것을 제안합니다.
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” 키워드 뒤에 삼중 비교를 허용하는 것입니다.:
for lower <= var < upper:
이는 비교식의 왼쪽 경계에서 시작하여 오른쪽 경계에서 끝나는, 연속된 정수 구간에 대한 반복을 일으킵니다. 사용된 정확한 비교 연산은 각 끝점에서 구간이 열려 있는지 닫혀 있는지, 그리고 정수가 오름차순으로 간주되는지 내림차순으로 간주되는지를 결정합니다.
이 구문은 표준 수학 표기법과 밀접하게 일치하므로, 현재의 range() 구문보다 Python 초보자에게 더 친숙할 가능성이 높습니다. 열린 구간과 닫힌 구간의 끝점은 똑같이 표현하기 쉬우며, 정수 구간을 뒤집는 것은 두 끝점을 맞바꾸고 비교를 뒤집는 것만으로 간단히 만들 수 있습니다. 게다가 이러한 루프의 의미는 기존 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 또는 greater_comp로 기술된 집합에 속하는 경우 +1 또는 -1의 스텝 크기로 나머지 정수들을 순회합니다. 그 후 실행은 마치 표현식이 다음과 같았던 것처럼 진행됩니다
for variable in iterator
여기서 “variable”은 루프의 변수를 가리키고 “iterator”는 주어진 정수 구간에 대해 생성된 이터레이터를 가리킵니다.
정수 for 루프에서 루프 변수가 취하는 값은 경계의 크기에 따라 일반 정수이거나 롱 정수일 수 있습니다. 정수 for 루프의 두 경계는 모두 실수 숫자 형(정수, 롱, 또는 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가 서로 충돌하지 않는다고 생각합니다. PEP 276은 주로 0에서 시작하는 반열린 범위(
range()의 쉬운 경우)를 다루는 반면, 이 PEP는 주로 그 외의 모든 경우를 단순화하는 데 주력하기 때문입니다. 그러나 이 PEP가 승인된다면, 정수 루프를 위한 새로운 더 간단한 구문이 PEP 276의 동기를 어느 정도 줄일 수 있습니다. - 정수 루프에 부동소수점 경계를 허용하는 것이 타당한지는 분명하지 않습니다. 부동소수점 값이 부정확한 값을 나타낸다면, 그것이 어떻게 정확한 정수 시퀀스를 결정하는 데 사용될 수 있겠습니까? 반면에 부동소수점 경계를 허용하지 않으면, 현재
range()에서 그것들을 사용하기 어려운 것과 마찬가지로 정수 for 루프에서floor()와ceiling()을 사용하기 어려워질 것입니다. 저희는 유연성 쪽으로 치우친 선택을 했지만, 이는 주어진 비교를 참으로 만드는 최소 및 최대 정수 값을 결정하는 데 있어 몇 가지 구현상의 어려움으로 이어질 수 있습니다. - int, long, float 이외의 타입도 경계로 허용해야 합니까? 다른 선택지는
int()로 모든 경계를 정수로 변환하고, 부동소수점뿐만 아니라 그렇게 변환될 수 있는 모든 것을 경계로 허용하는 것입니다. 그러나 이는 의미를 바꾸게 됩니다.0.3 <= x는int(0.3) <= x와 같지 않으며, 하한이 0.3인 루프가 0에서 시작하는 것은 혼란스러울 것입니다. 또한 일반적으로int(f)는f와 매우 동떨어질 수 있습니다.
구현
현재로서는 구현이 존재하지 않습니다. 구현은 큰 어려움을 야기하지 않을 것으로 예상됩니다. 필요하다면 새로운 구문은 각 “for” 키워드 뒤에 오는 일반 표현식을 파싱하고 그 표현식의 최상위 연산이 “in”인지 아니면 삼중 비교인지를 검사함으로써 인식될 수 있습니다. Python 컴파일러는 새로운 구문의 모든 인스턴스를 특수 이터레이터 객체의 항목들에 대한 루프로 변환할 것입니다.
참고 자료
Copyright
This document has been placed in the public domain.