[Final] PEP 218 - Adding a Built-In Set Object Type

원문 링크: PEP 218 - Adding a Built-In Set Object Type

상태: Final 유형: Standards Track 작성일: 31-Jul-2000

이 문서는 Python Enhancement Proposal (PEP) 218의 내용을 한국어 Python 개발자들이 이해하기 쉽도록 번역하고 정리한 것입니다. 이 PEP는 Python에 내장 Set 객체 타입을 추가하는 것에 대한 제안을 다룹니다.


PEP 218 – 내장 Set 객체 타입 추가

개요

이 PEP는 표준 Python 라이브러리에 Set 모듈을 추가하고, 이 모듈이 널리 사용될 경우 Set을 Python의 내장(built-in) 타입으로 만들 것을 제안합니다. 이 문서는 Set의 필요성과 함께, Set을 대신하여 딕셔너리를 사용하는 일반적인 방식이 왜 부적절한지 설명합니다. 또한, 내장 Set이 어떻게 작동할 것인지, 그리고 초기 Set 모듈이 어떻게 동작할 것인지에 대한 설명을 제공합니다. 마지막 섹션에서는 Set 및 Set 요소의 가변성(mutability)과 Set 모듈이 구현할 해결책에 대해 논의합니다.

도입 배경 (Rationale)

Set은 기본적인 수학적 구조이며, 알고리즘 사양에서 매우 흔하게 사용됩니다. 하지만 구현에서는 ‘올바른’ 구조임에도 불구하고 사용 빈도가 낮습니다. 프로그래머들은 리스트의 순서 정보가 무관하고 값(by-value)을 통한 조회가 빈번할 때에도 리스트를 대신 사용하는 경우가 많습니다. (대부분의 중간 규모 C 프로그램은 특정 항목이 존재하는지 여부를 확인하기 위해 malloc으로 할당된 벡터를 처음부터 끝까지 검색하는 경우가 많습니다.)

프로그래머들은 종종 Set을 “don’t care” 값을 가진 딕셔너리로 구현할 수 있다고 배웁니다. 이러한 “Set”에 항목을 추가하려면 “don’t care” 값을 할당하고, 멤버십은 dict.has_key를 사용하여 테스트하며, 항목은 del을 사용하여 삭제할 수 있습니다. 그러나 Set의 다른 주요 연산(합집합, 교집합, 차집합)은 키/값 쌍을 포함하는 딕셔너리의 경우 의미가 모호해지기 때문에 이러한 표현으로는 직접 지원되지 않습니다.

제안 (Proposal)

이 PEP의 장기적인 목표는 Python에 내장 set 타입을 추가하는 것입니다. 이 타입은 딕셔너리가 키/값 쌍의 순서 없는(unordered) 컬렉션인 것처럼, 고유한(unique) 값들의 순서 없는 컬렉션이 될 것입니다.

반복(iteration)과 컴프리헨션(comprehension)은 다음과 같이 직관적인 방식으로 구현될 것입니다:

for x in S:
    # S의 요소들을 임의의 순서로 순회합니다.
``````python
set(x**2 for x in S)
# S의 모든 요소의 제곱을 포함하는 Set을 생성합니다.

멤버십 테스트는 innot in을 사용하며, 기본적인 Set 연산은 오버로드된(overloaded) 연산자들의 조합으로 구현됩니다:

  • | : 합집합 (union)
  • & : 교집합 (intersection)
  • ^ : 대칭 차집합 (symmetric difference)
  • - : 비대칭 차집합 (asymmetric difference)
  • == != : 동등성 및 부등식 테스트
  • < <= >= > : 부분집합 및 상위집합 테스트

또한, 다음과 같은 메서드들이 제공됩니다:

  • S.add(x): Set에 “x”를 추가합니다.
  • S.update(s): 시퀀스 “s”의 모든 요소를 Set에 추가합니다.
  • S.remove(x): Set에서 “x”를 제거합니다. “x”가 없으면 LookupError 예외가 발생합니다.
  • S.discard(x): Set에 “x”가 있으면 제거하고, 없으면 아무것도 하지 않습니다.
  • S.pop(): 임의의 요소를 제거하고 반환합니다. 요소가 없으면 LookupError를 발생시킵니다.
  • S.clear(): 이 Set에서 모든 요소를 제거합니다.
  • S.copy(): 새로운 Set을 생성합니다.
  • s.issuperset(): 상위집합 관계를 확인합니다.
  • s.issubset(): 부분집합 관계를 확인합니다.

그리고 두 가지 새로운 내장(built-in) 변환 함수가 있습니다:

  • set(x): 컬렉션 “x”의 요소들을 포함하는 Set을 생성합니다.
  • frozenset(x): 컬렉션 “x”의 요소들을 포함하는 변경 불가능한(immutable) Set을 생성합니다.

참고: 합집합과 교집합에는 비트와이즈 연산자 |&를 사용할 것을 제안했습니다. 합집합에 +가 직관적일 수 있지만, 교집합에 *는 그렇지 않습니다 (질문받은 사람들 중 *의 동작을 정확히 추측한 사람은 거의 없었습니다). 요소를 Set에 추가하는 데 add 대신 +를 사용하는 것도 고려했지만, Guido van Rossum은 +가 다른 내장 타입에서는 대칭적이라는 점을 지적했습니다 (반면 *는 아닙니다). add를 사용함으로써 이 연산과 Set 합집합 간의 혼동을 피할 수 있습니다.

Set 표기법 (Set Notation)

이 PEP는 원래 {1,2,3}을 Set 표기법으로, {}를 빈 Set으로 제안했습니다. 그러나 Python 2.3의 sets.py를 통해 얻은 경험은 이러한 표기법이 필수적이지 않다는 것을 보여주었습니다. 또한, 딕셔너리의 즉각적인 인식을 저해할 위험도 있었습니다.

중괄호 표기법이 Set 컴프리헨션을 지원할 것으로 예상되었지만, Python 2.4는 제너레이터 표현식(generator expressions)을 제공하여 이 필요를 더 일반적인 방식으로 완전히 충족시켰습니다. (제너레이터 표현식에 대한 자세한 내용은 PEP 289를 참조하세요.)

결론적으로, Guido는 Set 구문(syntax)을 도입하지 않기로 결정했습니다. 그러나 이 문제는 Python 3000에서 다시 논의될 수 있습니다 (PEP 3000 참조).

역사 (History)

Set 사용 경험을 얻기 위해 Python 2.3에 순수 Python 모듈이 도입되었습니다. 이 구현을 기반으로 Python 2.4에 setfrozenset 타입이 도입되었습니다. 개선 사항은 다음과 같습니다:

  • frozenset을 위한 더 나은 해시(hash) 알고리즘
  • 더 압축된 pickle 형식 (값이 항상 True인 키:값 쌍 딕셔너리 대신 요소 목록만 저장)
  • __reduce__ 함수를 사용하여 딥 카피(deep copying) 자동화
  • BaseSet 개념 제거
  • union_update() 메서드가 update()로 변경
  • 가변(mutable) 및 변경 불가능(immutable) Set 간의 자동 변환 제거
  • _repr 메서드 제거 (새로운 내장 함수 sorted()로 대체)

Tim Peters는 클래스의 생성자가 단일 시퀀스를 인수로 받아 해당 시퀀스의 요소들로 Set을 채워야 한다고 생각했습니다. 그의 주장은 대부분의 경우 프로그래머들이 기존 시퀀스로부터 Set을 생성할 것이므로, 이 경우가 일반적이어야 한다는 것이었습니다. 그러나 이 경우 사용자는 알려진 값으로 Set을 초기화할 때 추가 괄호를 기억해야 했습니다:

>>> Set((1, 2, 3, 4)) # case 1

반면, 소수의 초보 Python 사용자들(모두 다른 언어에 매우 숙련된)의 피드백에 따르면, 사람들이 “괄호 없는(parenthesis-free)” 구문을 더 자연스럽게 느낄 것이라고 합니다:

>>> Set(1, 2, 3, 4) # case 2

궁극적으로, 초기화 함수가 단일 iterable 인수를 받는 첫 번째 전략이 채택되었습니다.

가변성 (Mutability)

이 제안에서 해결하기 가장 어려웠던 질문은 Set이 변경 가능한(mutable) 요소를 포함할 수 있어야 하는지 여부였습니다. 딕셔너리의 키는 빠르고 신뢰할 수 있는 조회를 지원하기 위해 변경 불가능해야 합니다. Set 요소가 변경 불가능하도록 요구하는 것은 쉬운 일이지만, 이는 Set의 Set(그래프 알고리즘 및 기타 응용 프로그램에서 널리 사용됨)을 불가능하게 만들 것입니다.

PEP 218의 초기 초안에는 단일 Set 타입만 있었지만, Python 2.3의 sets.py 구현에는 SetImmutableSet의 두 가지 타입이 있었습니다. Python 2.4에서는 새로운 내장 타입의 이름이 setfrozenset으로 지정되었으며, 이는 약간 덜 번거롭습니다.

sets 모듈에는 두 개의 클래스가 구현되어 있습니다. Set 클래스의 인스턴스는 요소의 추가 또는 제거를 통해 수정될 수 있으며, ImmutableSet 클래스는 “고정(frozen)”되어 변경 불가능한 요소 컬렉션을 가집니다. 따라서 ImmutableSet은 딕셔너리 키 또는 Set 요소로 사용될 수 있지만, 업데이트할 수는 없습니다. 두 가지 타입의 Set 모두 요소가 변경 불가능하고 해시 가능한(hashable) 객체여야 합니다. 이러한 내용은 setfrozenset 내장 타입에도 동일하게 적용됩니다.

이 문서는 퍼블릭 도메인으로 공개되었습니다.

⚠️ 알림: 이 문서는 AI를 활용하여 번역되었으며, 기술적 정확성을 보장하지 않습니다. 정확한 내용은 반드시 원문을 확인하시기 바랍니다.

Comments