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

Python 개선 제안 한국어 번역

PEP 814 – frozendict 내장 타입 추가

Author:
Victor Stinner <vstinner at python.org>, Donghee Na <donghee.na at python.org>
Discussions-To:
Discourse thread
Status:
Final
Type:
Standards Track
Created:
12-Nov-2025
Python-Version:
3.15
Post-History:
13-Nov-2025
Resolution:
11-Feb-2026

Table of Contents

번역·라이선스 안내

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

Important

This PEP is a historical document. The up-to-date, canonical documentation can now be found at frozendict.

×

See PEP 1 for how to propose changes.

초록

새로운 공개 불변 타입 frozendict가 builtins 모듈에 추가됩니다.

frozendict는 의도하지 않은 모든 수정을 막으므로 설계상 안전할 것으로 기대합니다. 이 추가는 CPython 표준 라이브러리뿐만 아니라, 신뢰할 수 있는 불변 딕셔너리 타입을 활용할 수 있는 서드파티 유지 관리자에게도 이점을 줍니다.

근거

제안하는 frozendict 타입은 다음과 같은 특징이 있습니다:

  • collections.abc.Mapping 프로토콜을 구현합니다.
  • 피클링을 지원합니다.

다음 사용 사례들은 불변 매핑이 바람직한 이유를 보여 줍니다:

  • 불변 매핑은 해시 가능하므로 딕셔너리 키나 집합 원소로 사용할 수 있습니다.
  • 이러한 해시 가능 속성 덕분에 @functools.lru_cache()로 데코레이트된 함수가 불변 매핑을 인자로 받을 수 있습니다. 불변 매핑과 달리, 이러한 함수에 일반 dict를 전달하면 오류가 발생합니다.
  • 불변 매핑을 함수 매개변수의 기본값으로 사용하면 가변 기본값 문제를 피할 수 있습니다.

PyPI에는 이미 서드파티 frozendict 및 frozenmap 패키지가 있으며, 이는 불변 매핑에 대한 수요가 있음을 입증합니다.

명세

새로운 공개 불변 타입 frozendict가 builtins 모듈에 추가됩니다. 이 타입은 dict의 서브클래스가 아니며 object를 직접 상속합니다.

생성

frozendict는 dict와 유사한 생성 API를 구현합니다:

  • frozendict()는 비어 있는 새 불변 매핑을 생성합니다.
  • frozendict(**kwargs)는 **kwargs로부터 매핑을 생성합니다. 예: frozendict(x=1, y=2).
  • frozendict(collection)은 전달된 컬렉션 객체로부터 매핑을 생성합니다. 전달되는 컬렉션 객체는 다음 중 하나일 수 있습니다:
    • dict,
    • 다른 frozendict,
    • 또는 키/값 튜플의 이터러블.
  • frozendict(collection, **kwargs)는 앞의 두 생성 방식을 결합합니다.

키는 해시 가능해야 하지만, 값은 해시 불가능해도 됩니다. 해시 가능한 값을 사용하면 해시 가능한 frozendict가 생성됩니다.

dict로부터 frozendict를 생성하는 것, 즉 frozendict(dict)의 복잡도는 O(n)입니다: 항목이 복사됩니다(얕은 복사).

삽입 순서는 유지됩니다.

이터레이션

frozendict는 표준 collections.abc.Mapping 프로토콜을 구현하므로, 예상되는 모든 이터레이션 방법을 지원합니다:

assert list(m.items()) == [('foo', 'bar')]
assert list(m.keys()) == ['foo']
assert list(m.values()) == ['bar']
assert list(m) == ['foo']

frozendict를 이터레이션할 때는 dict와 마찬가지로 삽입 순서를 따릅니다.

해싱과 비교

frozendict 인스턴스는 튜플 객체와 마찬가지로 해시 가능할 수 있습니다:

hash(frozendict(foo='bar'))  # works
hash(frozendict(foo=['a', 'b', 'c']))  # error, list is not hashable

해시값은 항목의 순서에 의존하지 않습니다. 해시값은 키와 값으로 계산됩니다. hash(frozendict)의 의사 코드:

hash(frozenset(frozendict.items()))

동등성 검사 역시 항목의 순서에 의존하지 않습니다. 예시:

>>> a = frozendict(x=1, y=2)
>>> b = frozendict(y=2, x=1)
>>> hash(a) == hash(b)
True
>>> a == b
True

frozendict를 dict와 비교할 수 있습니다. 예시:

>>> frozendict(x=1, y=2) == dict(x=1, y=2)
True

합집합 연산자

병합(|) 연산자로 두 frozendict를 합치거나, frozendict와 dict를 합칠 수 있습니다. 예시:

>>> frozendict(x=1) | frozendict(y=1)
frozendict({'x': 1, 'y': 1})
>>> frozendict(x=1) | dict(y=1)
frozendict({'x': 1, 'y': 1})

공통된 키가 있으면 오른쪽 피연산자의 값을 취합니다:

>>> frozendict(x=1, y=2) | frozendict(y=5)
frozendict({'x': 1, 'y': 5})

갱신 연산자 |=는 frozendict를 제자리에서 수정하지 않고, 새로운 frozendict를 생성합니다:

>>> d = frozendict(x=1)
>>> copy = d
>>> d |= frozendict(y=2)
>>> d
frozendict({'x': 1, 'y': 2})
>>> copy   # left unchanged
frozendict({'x': 1})

PEP 584 “Add Union Operators To dict”(dict에 합집합 연산자 추가)도 참고하십시오.

복사

frozendict.copy()는 얕은 복사본을 반환합니다. CPython에서는 단순히 같은 frozendict를 반환합니다(새 참조).

깊은 복사본을 얻으려면 copy.deepcopy()를 사용하십시오.

예시:

>>> import copy
>>> d = frozendict(mutable=[])
>>> shallow_copy = d.copy()
>>> deep_copy = copy.deepcopy(d)
>>> d['mutable'].append('modified')
>>> d
frozendict({'mutable': ['modified']})
>>> shallow_copy  # modified!
frozendict({'mutable': ['modified']})
>>> deep_copy     # unchanged
frozendict({'mutable': []})

타이핑

frozendict에 표준 타이핑 표기법을 사용할 수 있습니다:

m: frozendict[str, int] = frozendict(x=1)

표현

frozendict는 표현에 특별한 문법을 사용하지 않습니다. frozendict 인스턴스의 repr() 결과는 다음과 같습니다:

>>> frozendict(x=1, y=2)
frozendict({'x': 1, 'y': 2})

C API

다음 API를 추가합니다:

  • PyAnyDict_Check(op) 매크로
  • PyAnyDict_CheckExact(op) 매크로
  • PyFrozenDict_Check() 매크로
  • PyFrozenDict_CheckExact() 매크로
  • PyFrozenDict_New(collection) 함수
  • PyFrozenDict_Type

frozendict는 dict 서브클래스가 아니지만 PyDict_GetItemRef() 및 이와 비슷한 “PyDict_Get” 함수에 사용할 수 있습니다.

frozendict를 PyDict_SetItem()이나 PyDict_DelItem()에 전달하면 TypeError로 실패합니다. frozendict에 대한 PyDict_Check()는 거짓입니다.

dict와 frozendict의 차이점

  • dict에는 frozendict보다 많은 메서드가 있습니다:
    • __delitem__(key)
    • __setitem__(key, value)
    • clear()
    • pop(key)
    • popitem()
    • setdefault(key, value)
    • update(*args, **kwargs)
  • 모든 키와 값을 해시할 수 있으면 frozendict는 hash(frozendict)로 해시할 수 있습니다.

표준 라이브러리에서 frozendict를 적용할 수 있는 후보

저희는 frozendict를 도입하면 안전성을 높이고 의도하지 않은 수정을 설계 단계에서 막을 수 있는 표준 라이브러리 모듈을 여러 개 찾았습니다. 또한 아래에 나열된 것 외에도 추가적인 잠재적 사용 사례가 있다고 생각합니다. 그러나 이것이 각 모듈 유지 관리자의 승인 없이 이러한 변경을 하겠다는 의미는 아닙니다.

참고: 변수를 수정된 새 frozendict나 변경할 수 있는 새 dict에 다시 바인딩하는 것은 여전히 가능합니다.

Python 모듈

함수 결과에서 dict를 frozendict로 바꿉니다:

  • email.headerregistry: ParameterizedMIMEHeader.params() (MappingProxyType을 대체)
  • enum: EnumType.__members__() (MappingProxyType을 대체)

상수에서 dict를 frozendict로 바꿉니다:

  • _opcode_metadata: _specializations, _specialized_opmap, opmap
  • _pydatetime: specs (_format_time() 내부)
  • _pydecimal: _condition_map
  • bdb: _MonitoringTracer.EVENT_CALLBACK_MAP
  • dataclasses: _hash_action
  • dis: deoptmap, COMPILER_FLAG_NAMES
  • functools: _convert
  • gettext: _binary_ops, _c2py_ops
  • imaplib: Commands, Mon2num
  • json.decoder: _CONSTANTS, BACKSLASH
  • json.encoder: ESCAPE_DCT
  • json.tool: _group_to_theme_color
  • locale: locale_encoding_alias, locale_alias, windows_locale
  • opcode: _cache_format, _inline_cache_entries
  • optparse: _builtin_cvt
  • platform: _ver_stages, _default_architecture
  • plistlib: _BINARY_FORMAT
  • ssl: _PROTOCOL_NAMES
  • stringprep: b3_exceptions
  • symtable: _scopes_value_to_name
  • tarfile: PAX_NUMBER_FIELDS, _NAMED_FILTERS
  • token: tok_name, EXACT_TOKEN_TYPES
  • tomllib._parser: BASIC_STR_ESCAPE_REPLACEMENTS
  • typing: _PROTO_ALLOWLIST

frozendict 타입 허용:

  • builtins: eval() 및 exec() (globals 인자)

확장 모듈

상수에 dict 대신 frozendict 사용:

  • errno: errorcode

PEP 416 frozendict와의 관계

2012년(PEP 416) 이후 Python 생태계는 다음과 같이 발전했습니다:

  • asyncio는 2014년(Python 3.4)에 추가되었습니다.
  • 자유 스레딩은 2024년(Python 3.13)에 추가되었습니다.
  • concurrent.interpreters는 2025년(Python 3.14)에 추가되었습니다.

이제 불변 매핑을 공유해야 하는 사용 사례가 더 많아졌습니다.

PEP 416의 frozendict는 (PEP 603의 frozenmap처럼) 순서가 없었지만, 이제 frozendict는 삽입 순서를 유지합니다. frozendict는 Python 3.6부터 삽입 순서를 유지하는 dict 구현에 기반합니다.

frozendict를 추가하려던 최초의 동기는 Python에서 샌드박스를 구현하는 것이었습니다. 이 PEP에서는 더 이상 그렇지 않습니다.

types.MappingProxyType은 2012년(Python 3.3)에 추가되었습니다. 이 타입은 해시 가능하지 않으며 이를 상속할 수도 없습니다. 또한 예를 들어 gc.get_referents()를 사용하면 변경 가능한 원본 딕셔너리를 쉽게 가져올 수 있습니다.

PEP 603 frozenmap과의 관계

collections.frozenmap은 frozendict와 다른 특성을 가집니다:

  • frozenmap 항목에는 순서가 없지만, frozendict는 삽입 순서를 보존합니다.
  • frozenmap에는 다음과 같은 추가 메서드가 있습니다:
    • including(key, value)
    • excluding(key)
    • union(mapping=None, **kw)

    frozenmap을 변경하는 이 메서드들의 복잡도는 O(1)입니다.

  • 매핑 조회(mapping[key])의 복잡도는 frozenmap에서는 O(log n)이고 frozendict에서는 O(1)입니다.

참조 구현

스레드 안전성

frozendict가 일단 생성되면 얕은 불변성이 보장됩니다. 즉, 다른 스레드가 그 값을 수정하지 않는 한 동기화 없이도 스레드 간에 안전하게 공유할 수 있습니다.

기각된 아이디어

dict 상속

frozendict가 dict를 상속하면, dict 메서드를 호출하여 불변인 frozendict를 변경할 수 있게 됩니다. 예를 들어 dict.__setitem__(frozendict, key, value)를 호출할 수 있게 됩니다.

dict 메서드로 frozendict를 수정하는 것을 막을 수도 있겠지만, 그러려면 frozendict를 명시적으로 제외해야 하며 이는 dict 성능에 영향을 줄 수 있습니다. 또한 일부 메서드에서 frozendict를 제외하는 것을 잊어버릴 위험도 더 큽니다.

frozendict가 dict를 상속하지 않으면 이런 문제가 없습니다.

보류된 아이디어

frozendict 리터럴을 위한 새 문법

frozendict 리터럴을 작성하기 위한 다양한 문법이 제안되었습니다.

필요하다면 새 문법은 나중에 추가할 수 있습니다.

dict를 frozendict로 변환하는 메서드

dict.freeze()와 같이, 가변 dict를 O(1) 복잡도로 불변 frozendict로 변환하는 여러 방법이 제안되었습니다. 이 방식은 dict의 내용을 frozendict로 옮기는 것으로, 그 결과 dict는 비게 됩니다. 또 다른 방식은 “쓰기 시 복사(copy-on-write)”를 사용하는 것으로, dict가 처음 수정될 때에만 복사합니다.

이러한 메서드는 필요하다면 나중에 추가할 수 있지만, 지금 당장 추가할 필요는 없다고 생각합니다. 또한 이러한 메서드를 추가한다면 list/tuple과 set/frozenset에도 비슷한 메서드를 추가하는 것이 좋을 것입니다. PEP 351 (동결 프로토콜)도 참고하십시오.

타입 어노테이션

frozendict 타입을 정의하기 위해 class TD(TypedDict, frozen=True) 또는 Frozen[MyTypedDict]를 추가하자는 제안이 있었습니다.

이러한 타입은 필요하다면 나중에 추가할 수 있다고 생각합니다.

참고 문헌

감사의 글

이 PEP는 Yury Selivanov의 선행 작업(PEP 603)에 기반합니다.