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

Python 개선 제안 한국어 번역

PEP 416 – frozendict 내장 타입 추가

Author:
Victor Stinner <vstinner at python.org>
Status:
Rejected
Type:
Standards Track
Created:
29-Feb-2012
Python-Version:
3.3

Table of Contents

번역·라이선스 안내

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

거부 공지

이 PEP를 거부합니다. 이유는 여러 가지이며, 아래가 전부는 아닙니다.

  • Raymond Hettinger에 따르면 frozendict는 별로 사용되지 않습니다. frozendict를 사용하는 사람들도 전역 또는 클래스 수준의 “상수”를 선언할 때처럼 대개 힌트로만 사용합니다. 누구든 여전히 그 이름에 값을 대입할 수 있으므로 실제로 불변인 것은 아닙니다.
  • 가변 기본값을 피하는 관용구는 이미 있습니다.
  • PyPy에서 frozendict를 사용해 코드를 최적화할 수 있을지는 불확실합니다. 그 전에 다른 많은 것이 먼저 바뀌어야 합니다. 컴파일 시간 조회 전반도 마찬가지입니다.
  • 여러 스레드는 공유 dict를 변경하지 않기로 관례상 합의할 수 있으므로, 이를 강제할 필요는 크지 않습니다. 여러 프로세스는 dict를 공유할 수 없습니다.
  • 범위가 제한적이라 하더라도 Python으로 작성된 보안 샌드박스를 추가하는 것은 많은 사람이 탐탁지 않게 여깁니다. 샌드박스가 실제로 안전하다는 것을 증명하는 일 자체가 본질적으로 어렵기 때문입니다. 이 때문에 당분간 표준 라이브러리에 보안 샌드박스를 추가하지 않을 것이므로, 이 사용 사례는 PEP의 범위를 벗어납니다.

한편, 기존의 읽기 전용 dict 프록시를 내장 타입으로 노출하는 것은 좋아 보입니다. (생성자를 호출할 수 있도록 변경해야 할 것입니다.) GvR.

업데이트 (2012-04-15): Python 3.3의 types 모듈에 새로운 MappingProxyType 타입이 추가되었습니다.

초록

새로운 frozendict 내장 타입을 추가합니다.

근거

frozendict는 읽기 전용 매핑입니다. 키를 추가하거나 제거할 수 없으며, 키는 항상 같은 값에 매핑됩니다. 하지만 frozendict의 값은 해시 불가능해도 됩니다. frozendict는 모든 값이 해시 가능한 경우에만 해시 가능합니다.

사용 사례:

  • 기본 설정과 같은 불변 전역 변수입니다.
  • 함수 매개변수의 기본값입니다. 가변 기본 인자 문제를 피할 수 있습니다.
  • 캐시 구현: frozendict를 사용하여 함수 키워드를 저장할 수 있습니다. frozendict는 매핑의 키나 집합의 원소로 사용할 수 있습니다.
  • frozendict를 여러 스레드나 프로세스가 공유할 때, 특히 해시 가능한 frozendict라면 잠금이 필요 없습니다. 또한 코루틴(제너레이터 + greenlet)이 전역 상태를 변경하지 못하게 막는 데에도 도움이 됩니다.
  • 매핑이 읽기 전용이므로 frozendict 조회는 런타임이 아닌 컴파일 시간에 수행할 수 있습니다. 디버그 빌드 전용 코드처럼 컴파일 시 조건부 코드를 제거할 때 전처리기 대신 frozendict를 사용할 수 있습니다.
  • frozendict는 보안 모듈을 위한 읽기 전용 객체 프록시를 구현하는 데 도움이 됩니다. 예를 들어, frozendict 타입을 __builtins__ 매핑이나 type.__dict__ 용도로 사용할 수 있을 것입니다. 이는 frozendict가 PyDict C API와 호환되기 때문에 가능합니다.
  • frozendict를 사용하면 경우에 따라 읽기 전용 프락시가 필요하지 않습니다. frozendict에서 항목을 가져오는 것은 빠른 조회이지만 프락시는 함수 호출이 필요하므로, frozendict가 프락시보다 빠릅니다.

제약 조건

  • frozendict는 Mapping(매핑) 추상 베이스 클래스를 구현해야 합니다
  • frozendict의 키와 값은 정렬할 수 없는 것이어도 됩니다
  • 모든 키와 값이 해시 가능하면 frozendict도 해시 가능합니다
  • frozendict의 해시는 항목이 생성된 순서에 의존하지 않습니다

구현

  • PyDictObject를 기반으로 하되 해시를 저장하는 “Py_hash_t hash;” 필드를 추가로 가진 PyFrozenDictObject 구조체를 추가합니다
  • frozendict.__hash__()는 hash(frozenset(self.items()))를 사용하여 구현되며, 그 결과를 비공개 해시 속성에 캐시합니다
  • frozendict를 collections.abc.Mapping으로 등록합니다
  • frozendict는 PyDict_GetItem()과 함께 사용할 수 있지만, PyDict_SetItem()과 PyDict_DelItem()은 TypeError를 발생시킵니다

레시피: 해시 가능한 dict

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로 구현한 타입은 더 빠르기도 합니다.

PEP 351은 거부되었습니다.

PEP 351은 객체를 동결하려고 하므로 가변 객체를 (다른 타입을 사용해) 불변 객체로 변환할 수 있습니다. frozendict는 아무것도 변환하지 않습니다. 해시 가능하지 않은 값이 있으면 hash(frozendict)는 TypeError를 발생시킵니다. 객체를 동결하는 것은 이 PEP의 목적이 아닙니다.

대안: dictproxy

Python에는 type.__dict__ getter 디스크립터가 사용하는 내장 dictproxy 타입이 있습니다. 이 타입은 공개되어 있지 않습니다. dictproxy는 딕셔너리의 읽기 전용 뷰이지만, 읽기 전용 매핑은 아닙니다. 딕셔너리가 수정되면 dictproxy도 수정됩니다.

dictproxy는 ctypes와 파이썬 C API를 통해 사용할 수 있습니다. 예를 들어 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와 비슷하지만 완전한 읽기 전용은 아닙니다. 비공개 내부 dict에 접근할 수 있습니다. __hash__를 구현하지 않으며, __init__()을 다시 호출해 매핑을 수정할 수 있다는 구현상의 문제가 있습니다.
  • PyWebmail에는 ImmutableDict 타입이 있습니다: webmail.utils.ImmutableDict. 키와 값이 해시 가능하면 해시 가능합니다. 진정한 읽기 전용은 아닙니다: 내부 dict가 공개 속성입니다.
  • remember 프로젝트: remember.dicts.FrozenDict. 캐시를 구현하는 데 사용됩니다. FrozenDict에 함수 콜백을 저장합니다. FrozenDict는 해시 가능할 수 있습니다. dict를 복사하지 않고 dict로부터 FrozenDict를 생성하는 supply_dict() 클래스 메서드가 추가로 있습니다. 이 메서드는 주어진 dict를 그대로 내부 dict로 저장합니다. 구현상의 문제: __init__()을 호출해 매핑을 수정할 수 있으며, 항목 생성 순서에 따라 해시가 달라질 수 있습니다. 이 매핑은 진정한 읽기 전용이 아닙니다: 내부 dict에 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)입니다. 키와 값이 해시 가능하면 해시 가능합니다. 다음 프로젝트에 포함되어 있습니다:
  • George Sakkis가 작성한 gsakkis-utils 프로젝트에는 frozendict 타입이 포함되어 있습니다: datastructs.frozendict
  • characters: scripts/python/frozendict.py. 해시 가능합니다. __init__()이 __init__을 None으로 설정합니다.
  • 구버전 NLTK(1.x): nltk.util.frozendict. 키와 값은 해시 가능해야 합니다. __init__()을 두 번 호출해 매핑을 수정할 수 있습니다. frozendict는 객체를 “고정”하는 데 사용됩니다.

해시 가능한 dict: dict를 상속하고 __hash__ 메서드만 추가합니다.

링크