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

Python 개선 제안 한국어 번역

PEP 265 – 값으로 딕셔너리 정렬

Author:
Grant Griffin <g2 at iowegian.com>
Status:
Rejected
Type:
Standards Track
Created:
08-Aug-2001
Python-Version:
2.2
Post-History:


Table of Contents

번역·라이선스 안내

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

초록

이 PEP는 딕셔너리에 대한 “값으로 정렬” 연산을 제안합니다. 주요 이점은 현재 형태로는 초보자가 이해하기 어렵고 누구나 구현하기 번거로운 일반적인 Python 관용구에 대해 “배터리 포함” 지원을 제공한다는 점입니다.

BDFL 선언

이 PEP는 그 필요성이 Py2.4의 sorted() 내장 함수로 대부분 충족되었으므로 거부합니다.:

>>> sorted(d.iteritems(), key=itemgetter(1), reverse=True)
[('b', 23), ('d', 17), ('c', 5), ('a', 2), ('e', 1)]

또는 키만 대상으로:

sorted(d, key=d.__getitem__, reverse=True)
['b', 'd', 'c', 'a', 'e']

또한 Python 2.5의 heapq.nlargest() 함수는 가장 높은 값을 가진 항목 중 몇 개만 찾는 일반적인 사용 사례를 해결합니다.:

>>> nlargest(2, d.iteritems(), itemgetter(1))
[('b', 23), ('d', 17)]

동기

딕셔너리의 일반적인 사용법 중 하나는 어떤 항목이 처음 나타났을 때 d[key]의 값을 1로 설정한 다음, 이후 나타날 때마다 그 값을 증가시켜 출현 횟수를 세는 것입니다. 이를 수행하는 방법은 여러 가지가 있지만, get() 메서드가 가장 간결합니다.:

d[key] = d.get(key, 0) + 1

모든 출현 횟수를 센 후에는 결과 딕셔너리의 출현 항목을 출현 횟수순으로, 흔히 가장 큰 값부터 출력하는 것이 일반적인 사용법입니다.

따라서 딕셔너리의 항목을 값으로 정렬해야 합니다. Python에서 이를 수행하는 표준적인 방법은 먼저 d.items()을 사용하여 딕셔너리 항목의 목록을 얻은 다음, 각 항목 튜플의 순서를 (key, value)에서 (value, key)로 뒤집고, 그 목록을 정렬하는 것입니다. Python은 튜플의 첫 번째 항목을 기준으로 목록을 정렬하므로, 그 결과 (순서가 뒤집힌) 항목 목록은 값으로 정렬됩니다. 원한다면 그다음 목록을 뒤집고 튜플을 다시 (key, value)로 되돌릴 수 있습니다. (그러나 제가 경험한 바로는 대부분의 목적에 순서가 뒤집힌 튜플을 그대로 사용해도 충분합니다. 예를 들어 목록을 출력하는 경우가 그렇습니다.)

예를 들어, 다음과 같은 출현 횟수가 주어졌을 때:

>>> d = {'a':2, 'b':23, 'c':5, 'd':17, 'e':1}

다음과 같이 할 수 있습니다.:

>>> items = [(v, k) for k, v in d.items()]
>>> items.sort()
>>> items.reverse()             # so largest is first
>>> items = [(k, v) for v, k in items]

그 결과는 다음과 같습니다.:

>>> items
[('b', 23), ('d', 17), ('c', 5), ('a', 2), ('e', 1)]

이는 목록이 값 순서로, 가장 큰 값부터 표시됨을 보여 줍니다. (이 경우 'b'가 가장 많이 나타난 것으로 확인되었습니다.)

이는 잘 작동하지만 두 가지 측면에서 “사용하기 어렵습니다”. 첫째, 이 관용구는 숙련된 Python 프로그래머에게는 알려져 있지만 초보자에게는 알고리즘 측면(항목 튜플의 순서를 뒤집는 것)에서도, 구현 측면(고급 Python 기능인 리스트 컴프리헨션을 사용하는 것)에서도 전혀 자명하지 않습니다. 둘째, 많은 “잡다한 코드”를 반복해서 입력해야 하므로 지루할 뿐 아니라 실수도 발생합니다.

따라서 Python이 초보자도 쉽게 이해할 수 있고(더 정확히는 이해할 필요가 없도록 하며) 누구나 더 쉽게 사용할 수 있는, 값을 기준으로 딕셔너리를 정렬하는 메서드를 제공하는 편이 낫습니다.

근거

Tim Peters가 지적했듯이 이러한 종류의 기능은 모든 사람에게 모든 것을 제공하려는 문제를 불러옵니다. 따라서 “최적의 지점”을 겨냥할 수 있도록 그 범위를 제한하겠습니다. 특수한 경우(예: 사용자 지정 비교 함수를 통한 정렬)는 물론 현재의 방법을 사용하여 “수동으로” 처리할 수 있습니다.

다음은 몇 가지 간단한 가능성입니다.

딕셔너리의 items() 메서드에 완전한 하위 호환성을 제공하는 기본값이 있는 새 매개변수를 추가할 수 있습니다.:

(1) items(sort_by_values=0, reversed=0)

또는 다음과 같이만 할 수도 있습니다.:

(2) items(sort_by_values=0)

목록을 뒤집는 일은 충분히 쉽기 때문입니다.

또는 items()가 단순히 (key, value) 순서를 제어할 수 있도록 할 수도 있습니다.:

(3) items(values_first=0)

다시 말하지만, 완전히 하위 호환됩니다. 다른 방법들보다 작업량은 적지만, 값 기준 정렬 문제에서 가장 복잡하고 까다로운 부분인 항목 튜플 순서 뒤집기를 적어도 간소화합니다. 이를 사용하는 방법은 매우 간단합니다:

items = d.items(1)
items.sort()
items.reverse()         # (if desired)

앞의 세 가지 방법의 주요 단점은 기본 매개변수를 처리해야 하므로 매개변수가 없는 items()의 경우에 추가 오버헤드가 발생한다는 점입니다. (그러나 items()가 주로 값 기준 정렬 목록을 만드는 데 사용된다고 가정하면, 실제로는 큰 단점이 아닙니다.)

또는 어떤 방식으로든 “정렬”을 구현하는 새 딕셔너리 메서드를 추가할 수도 있습니다. 이 방법은 두 가지 장점을 제공합니다. 첫째, items()메서드에 오버헤드를 추가하지 않습니다. 둘째, 초보자에게 아마 더 접근하기 쉽습니다. 초보자가 딕셔너리를 정렬하는 메서드를 찾을 때 이 메서드를 만나기를 기대할 수 있으며, 값 기준 정렬을 수행하기 위해 튜플 순서 뒤집기와 리스트 정렬의 세부 사항을 이해할 필요가 없습니다.

키/값 기준 정렬과 정방향/역방향 정렬이라는 네 가지 기본 가능성을 지원하려면 다음 메서드를 추가할 수 있습니다:

(4) sorted_items(by_value=0, reversed=0)

실제로 가장 일반적인 경우는 by_value=1, reversed=1일 것이라고 생각하지만, 여기 제시한 기본값은 사용자가 겪는 놀라움을 줄일 수 있습니다. sorted_items()items()에 이어 sort()를 호출한 것과 동일하게 동작합니다.

마지막으로(최후의 수단으로) 다음을 사용할 수 있습니다:

(5) items_sorted_by_value(reversed=0)

구현

제안된 딕셔너리 메서드는 반드시 C로 구현해야 합니다. Python의 기존 메커니즘에 몇 번의 호출만 추가하면 되므로 구현은 상당히 간단할 것으로 예상됩니다.

우려 사항

가능성 1부터 3까지에서 이미 다룬 실행 시간 오버헤드를 제외하면, 이 제안에 대한 우려는 아마도 “기능 비대화(feature bloat)” 및/또는 “코드 비대화(code bloat)”의 범주에 속할 것입니다. 하지만 여기서 제시된 제안 중 여럿이 비대화를 상당히 최소화하여, 비대화와 “부가 가치” 사이의 좋은 절충안을 이끌어낼 것이라고 생각합니다.

Tim Peters는 이를 C로 구현하더라도 오늘날 Python으로 구현하는 것보다 크게 더 빠르지는 않을 수 있다고 지적한 바 있습니다. 하지만 여기서 의도하는 주요 이점은 “속도”가 아니라 “접근성”과 “사용 편의성”입니다. 따라서 눈에 띄게 느려지지만 않는다면(일반 items()의 경우 속도는 고려 사항이 될 필요가 없습니다.

참고 문헌

“발생 횟수 세기(counting occurrences)”라는 관련 스레드가 2001년 8월 comp.lang.python에 등장했습니다. 여기에는 값별 정렬(sort-by-value) 문제를 재사용 가능한 Python 함수와 클래스로 구현하여 체계화하는 접근 방식의 예시가 포함되어 있었습니다.