PEP 416 – frozendict 내장 타입을 추가합니다.
- Author:
- Victor Stinner <vstinner at python.org>
- Status:
- Rejected
- Type:
- Standards Track
- Created:
- 29-Feb-2012
- Python-Version:
- 3.3
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
거부 통지
이 PEP를 거부합니다. 여러 가지 이유가 있습니다(전부 열거한 것은 아닙니다).
- Raymond Hettinger에 따르면 frozendict의 사용률은 낮습니다. 이를 사용하는 사람들도 전역 또는 클래스 수준의 “상수”를 선언하는 것처럼 대개 힌트로만 사용합니다. 누구나 여전히 해당 이름에 할당할 수 있으므로 실제로 불변인 것은 아닙니다.
- 변경 가능한 기본값을 피하기 위한 기존 관용구가 있습니다.
- PyPy에서 frozendict를 사용해 코드를 최적화할 가능성은 확실하지 않으며, 먼저 다른 많은 부분이 변경되어야 합니다. 일반적으로 컴파일 시간 조회에도 같은 문제가 적용됩니다.
- 여러 스레드는 공유 딕셔너리를 변경하지 않기로 관례상 합의할 수 있으므로, 이를 강제할 필요성은 크지 않습니다. 여러 프로세스는 딕셔너리를 공유할 수 없습니다.
- 범위가 제한되어 있더라도 Python으로 작성된 보안 샌드박스를 추가하는 것은, 해당 샌드박스가 실제로 안전하다는 것을 끝내 입증하기가 본질적으로 어렵기 때문에 많은 사람에게 달갑지 않게 여겨집니다. 따라서 당분간 표준 라이브러리에 이를 추가하지 않을 것이므로, 이 사용 사례는 PEP의 범위를 벗어납니다.
반면 기존의 읽기 전용 딕셔너리 프록시를 내장 타입으로 노출하는 것은 좋은 생각인 것 같습니다. (생성자를 호출할 수 있도록 변경해야 합니다.) GvR.
Update (2012-04-15): Python 3.3의 types 모듈에 새로운 MappingProxyType 타입이 추가되었습니다.
초록
새로운 frozendict 내장 타입을 추가합니다.
근거
frozendict는 읽기 전용 매핑입니다. 키를 추가하거나 제거할 수 없으며, 키는 항상 동일한 값에 매핑됩니다. 그러나 frozendict 값은 해시 불가능할 수 있습니다. 모든 값이 해시 가능할 경우에만 frozendict도 해시 가능합니다.
사용 사례:
- 기본 설정과 같은 불변 전역 변수입니다.
- 함수 매개변수의 기본값입니다. 변경 가능한 기본 인자의 문제를 피할 수 있습니다.
- 캐시를 구현할 수 있습니다. frozendict를 사용해 함수 키워드를 저장할 수 있으며, 매핑의 키나 집합의 멤버로 사용할 수 있습니다.
- frozendict는 여러 스레드나 프로세스에서 공유될 때, 특히 해시 가능한 frozendict일 때 잠금의 필요성을 없앱니다. 또한 코루틴(제너레이터 + 그린릿)이 전역 상태를 수정하지 못하도록 하는 데 도움이 됩니다.
- 매핑이 읽기 전용이므로 frozendict 조회는 런타임 대신 컴파일 시간에 수행할 수 있습니다. 컴파일 시 전처리기 대신 frozendict를 사용해 조건부 코드를 제거할 수 있으며, 예를 들어 디버그 빌드에 특화된 코드가 이에 해당합니다.
- frozendict는 보안 모듈을 위한 읽기 전용 객체 프록시를 구현하는 데 도움이 됩니다. 예를 들어, __builtins__ 매핑 또는 type.__dict__에 frozendict 타입을 사용할 수 있습니다. frozendict가 PyDict C API와 호환되기 때문에 이것이 가능합니다.
- frozendict는 일부 경우 읽기 전용 프록시가 필요하지 않도록 합니다. frozendict에서 항목을 가져오는 것은 빠른 조회인 반면 프록시는 함수 호출이 필요하므로, frozendict가 프록시보다 빠릅니다.
제약 조건
- frozendict는 매핑 추상 베이스 클래스를 구현해야 합니다.
- frozendict의 키와 값은 순서를 지정할 수 없어도 됩니다.
- 모든 키와 값이 해시 가능하면 frozendict는 해시 가능합니다.
- frozendict의 해시는 항목 생성 순서에 의존하지 않습니다.
구현
- 추가 필드 “Py_hash_t hash;”를 포함하고 PyDictObject를 기반으로 하는 PyFrozenDictObject 구조체를 추가하십시오.
- frozendict.__hash__()는 hash(frozenset(self.items()))를 사용하여 구현하며, 그 결과를 private hash 특성에 캐시합니다.
- frozendict를 collections.abc.Mapping으로 매핑으로 등록하십시오
- frozendict는 PyDict_GetItem()과 함께 사용할 수 있지만, PyDict_SetItem() 및 PyDict_DelItem()은 TypeError를 발생시킵니다.
레시피: 해시 가능한 딕셔너리
frozendict가 해시 가능하도록 보장하려면 frozendict를 생성하기 전에 값을 확인할 수 있습니다.:
import itertools
def hashabledict(*args, **kw):
# ensure that all values are hashable
for key, value in itertools.chain(args, kw.items()):
if isinstance(value, (int, str, bytes, float, frozenset, complex)):
# avoid the compute the hash (which may be slow) for builtin
# types known to be hashable for any value
continue
hash(value)
# don't check the key: frozendict already checks the key
return frozendict.__new__(cls, *args, **kw)
이의 제기
namedtuple은 frozendict의 요구 사항에 맞을 수 있습니다.
namedtuple은 매핑이 아니며 Mapping 추상 베이스 클래스를 구현하지 않습니다.
frozendict는 디스크립터를 사용하여 Python으로 구현할 수 있으며, frozendict는 실질적으로 상수이기만 하면 됩니다.
frozendict를 Python 보안 강화(보안 목적)에 사용하려면 C로 구현해야 합니다. C로 구현된 타입은 더 빠르기도 합니다.
The PEP 351 은 거부되었습니다.
객체를 고정하려는 PEP 351은 가변 객체를 불변 객체로 변환할 수 있습니다(다른 형식을 사용합니다). frozendict는 아무것도 변환하지 않습니다. 값이 해시 가능하지 않으면 hash(frozendict)는 TypeError를 발생시킵니다. 객체를 고정하는 것은 이 PEP의 목적이 아닙니다. 객체를 동결하는 것은 이 PEP의 목적이 아닙니다.
대안: dictproxy
Python에는 type.__dict__ getter 디스크립터에서 사용하는 내장 dictproxy 타입이 있습니다. 이 타입은 공개되지 않습니다. dictproxy는 딕셔너리의 읽기 전용 뷰이지만 읽기 전용 매핑은 아닙니다. 딕셔너리가 수정되면 dictproxy도 수정됩니다.
ctypes와 Python C API를 사용하여 dictproxy를 사용할 수 있습니다. 예를 들어 Ikkei Shimomura가 작성한 make dictproxy object via ctypes.pythonapi and type() (Python recipe 576540)를 참조하십시오. 이 레시피에는 dictproxy에 연결된 딕셔너리를 수정하여 dictproxy가 “가변”인지 확인하는 테스트가 포함되어 있습니다.
그러나 변경 가능한 속성이 문제가 되지 않는 일부 경우에는 딕셔너리 복사를 피하기 위해 dictproxy가 유용할 수 있습니다.
기존 구현
화이트리스트 접근 방식입니다.
- Aristotelis Mikropoulos가 작성한 Implementing an Immutable Dictionary (Python recipe 498072)입니다. frozendict와 유사하지만 진정한 읽기 전용은 아닙니다. 이 비공개 내부 딕셔너리에 접근할 수 있습니다. __hash__ 구현을 제공하지 않으며, __init__() 호출을 다시 수행하여 매핑을 수정할 수 있다는 구현상의 문제가 있습니다.
- PyWebmail에는 다음과 같은 ImmutableDict 형식이 있습니다: webmail.utils.ImmutableDict. 키와 값이 해시 가능하면 해시 가능합니다. 진정한 읽기 전용은 아닙니다. 내부 딕셔너리가 공개 속성입니다.
- remember 프로젝트: remember.dicts.FrozenDict. 캐시를 구현하는 데 사용됩니다. FrozenDict는 함수 콜백을 저장하는 데 사용됩니다. FrozenDict는 해시 가능할 수 있습니다. 딕셔너리를 복사하지 않고 딕셔너리에서 FrozenDict를 생성하는 추가 supply_dict() 클래스 메서드가 있습니다. 딕셔너리를 내부 딕셔너리로 저장합니다. 구현상의 문제가 있습니다. __init__() 호출로 매핑을 수정할 수 있으며, 해시는 항목 생성 순서에 따라 달라질 수 있습니다. 매핑은 진정한 읽기 전용이 아닙니다. Python에서 내부 딕셔너리에 접근할 수 있습니다.
블랙리스트 방식: dict를 상속하고 예외를 발생시키도록 쓰기 메서드를 재정의합니다. 진정한 읽기 전용은 아닙니다. 이러한 “동결된 딕셔너리”에서 dict 메서드를 호출하여 수정하는 것이 여전히 가능합니다.
- brownie: brownie.datastructures.ImmutableDict. 키와 값이 해시 가능하면 해시 가능합니다. werkzeug 프로젝트에도 동일한 코드가 있습니다: werkzeug.datastructures.ImmutableDict. ImmutableDict는 전역 상수(설정 옵션)에 사용됩니다. Flask 프로젝트는 기본 설정에 werkzeug의 ImmutableDict를 사용합니다.
- SQLAlchemy 프로젝트: sqlalchemy.util.immutabledict. 해시 가능하지 않으며 union()이라는 추가 메서드가 있습니다. immutabledict는 매핑을 기대하는 일부 함수의 매개변수 기본값으로 사용됩니다. 예: SqlSoup.map()에서 mapper_args=immutabledict()입니다.
- Oren Tirosh가 작성한 Frozen dictionaries (Python recipe 414283)입니다. 키와 값이 해시 가능하면 해시 가능합니다. 다음 프로젝트에 포함되어 있습니다:
- lingospot: frozendict/frozendict.py
- factor-graphics: python/fglib/util_ext_frozendict.py에 frozendict 형식이 있습니다.
- George Sakkis가 작성한 gsakkis-utils 프로젝트에는 datastructs.frozendict 형식의 frozendict가 포함되어 있습니다.
- characters: scripts/python/frozendict.py. 해시 가능합니다. __init__() 실행 시 __init__ 값이 None으로 설정됩니다.
- Old NLTK (1.x): nltk.util.frozendict. 키와 값은 해시 가능해야 합니다. __init__() 호출을 두 번 수행하여 매핑을 수정할 수 있습니다. frozendict는 객체를 “고정”하는 데 사용됩니다.
해시 가능한 dict: dict를 상속하고 __hash__ 메서드만 추가합니다.
- pypy.rpython.lltypesystem.lltype.frozendict. 해시 가능하지만 매핑의 수정을 막지는 않습니다.
- factor-graphics: python/fglib/util_ext_frozendict.py에 있는 hashabledict 타입
링크
- Issue #14162: PEP 416: 내장 frozendict 타입 추가
- PEP 412: 키 공유 딕셔너리 (issue #13903)
- PEP 351: 동결 프로토콜
- 불변 딕셔너리를 사용해야 하는 이유와 PEP 351의 핵심적인 오해
- ctypes.pythonapi와 type()을 통해 dictproxy 객체 만들기 (Python 레시피 576540) 작성자: Ikkei Shimomura입니다.
- C 확장을 사용하여 읽기 전용 객체 프록시를 구현하는 Python 보안 모듈:
Copyright
This document has been placed in the public domain.