134 lines
4.6 KiB
Plaintext
134 lines
4.6 KiB
Plaintext
PEP: 416
|
|
Title: Add a frozendict builtin type
|
|
Version: $Revision$
|
|
Last-Modified: $Date$
|
|
Author: Victor Stinner <victor.stinner@gmail.com>
|
|
Status: Draft
|
|
Type: Standards Track
|
|
Content-Type: text/x-rst
|
|
Created: 29-February-2012
|
|
Python-Version: 3.3
|
|
|
|
|
|
Abstract
|
|
========
|
|
|
|
Add a new frozendict builtin type.
|
|
|
|
|
|
Rationale
|
|
=========
|
|
|
|
A frozendict is a read-only mapping: a key cannot be added nor removed, and a
|
|
key is always mapped to the same value. However, frozendict values can be not
|
|
hashable. A frozendict is hashable if and only if all values are hashable.
|
|
|
|
Use cases:
|
|
|
|
* frozendict lookup can be done at compile time instead of runtime because the
|
|
mapping is read-only. frozendict can be used instead of a preprocessor to
|
|
remove conditional code at compilation, like code specific to a debug build.
|
|
* hashable frozendict can be used as a key of a mapping or as a member of set.
|
|
frozendict can be used to implement a cache.
|
|
* frozendict avoids the need of a lock when the frozendict is shared
|
|
by multiple threads or processes, especially hashable frozendict. It would
|
|
also help to prohibe coroutines (generators + greenlets) to modify the
|
|
global state.
|
|
* frozendict helps to implement read-only object proxies for security modules.
|
|
For example, it would be possible to use frozendict type for __builtins__
|
|
mapping or type.__dict__. This is possible because frozendict is compatible
|
|
with the PyDict C API.
|
|
* frozendict avoids the need of a read-only proxy in some cases. frozendict is
|
|
faster than a proxy because getting an item in a frozendict is a fast lookup
|
|
whereas a proxy requires a function call.
|
|
* use a frozendict as the default value of function argument: avoid the
|
|
problem of mutable default argument.
|
|
|
|
|
|
Constraints
|
|
===========
|
|
|
|
* frozendict has to implement the Mapping abstract base class
|
|
* frozendict keys and values can be unorderable
|
|
* a frozendict is hashable if all keys and values are hashable
|
|
* frozendict hash does not depend on the items creation order
|
|
|
|
|
|
Implementation
|
|
==============
|
|
|
|
* Add a PyFrozenDictObject structure based on PyDictObject with an extra
|
|
"Py_hash_t hash;" field
|
|
* frozendict.__hash__() is implemented using hash(frozenset(self.items())) and
|
|
caches the result in its private hash attribute
|
|
* Register frozendict as a collections.abc.Mapping
|
|
* frozendict can be used with PyDict_GetItem(), but PyDict_SetItem() and
|
|
PyDict_DelItem() raise a TypeError
|
|
|
|
|
|
Recipe: hashable dict
|
|
======================
|
|
|
|
To ensure that a a frozendict is hashable, values can be checked
|
|
before creating the 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)
|
|
|
|
|
|
Objections
|
|
==========
|
|
|
|
*namedtuple may fit the requiements of a frozendict.*
|
|
|
|
A namedtuple is not a mapping, it does not implement the Mapping abstract base
|
|
class.
|
|
|
|
*frozendict can be implemented in Python using descriptors" and "frozendict
|
|
just need to be practically constant.*
|
|
|
|
If frozendict is used to harden Python (security purpose), it must be
|
|
implemented in C. A type implemented in C is also faster.
|
|
|
|
*The PEP 351 was rejected.*
|
|
|
|
The PEP 351 tries to freeze an object and so may convert a mutable object to an
|
|
immutable object (using a different type). frozendict doesn't convert anything:
|
|
hash(frozendict) raises a TypeError if a value is not hashable. Freezing an
|
|
object is not the purpose of this PEP.
|
|
|
|
|
|
Links
|
|
=====
|
|
|
|
* PEP 412: Key-Sharing Dictionary
|
|
(`issue #13903 <http://bugs.python.org/issue13903>`_)
|
|
* PEP 351: The freeze protocol
|
|
* `The case for immutable dictionaries; and the central misunderstanding of PEP 351 <http://www.cs.toronto.edu/~tijmen/programming/immutableDictionaries.html>`_
|
|
* `Frozen dictionaries (Python recipe 414283) <http://code.activestate.com/recipes/414283-frozen-dictionaries/>`_
|
|
by Oren Tirosh
|
|
* Python security modules implementing read-only object proxies using a C
|
|
extension:
|
|
|
|
* `pysandbox <https://github.com/haypo/pysandbox/>`_
|
|
* `mxProxy <http://www.egenix.com/products/python/mxBase/mxProxy/>`_
|
|
* `zope.proxy <http://pypi.python.org/pypi/zope.proxy>`_
|
|
* `zope.security <http://pypi.python.org/pypi/zope.security>`_
|
|
|
|
|
|
Copyright
|
|
=========
|
|
|
|
This document has been placed in the public domain.
|
|
|