Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 54 additions & 3 deletions Doc/c-api/dict.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

.. _dictobjects:

Dictionary Objects
Dictionary objects
------------------

.. index:: pair: object; dictionary
Expand Down Expand Up @@ -444,7 +444,7 @@ Dictionary Objects
.. versionadded:: 3.12


Dictionary View Objects
Dictionary view objects
^^^^^^^^^^^^^^^^^^^^^^^

.. c:function:: int PyDictViewSet_Check(PyObject *op)
Expand Down Expand Up @@ -490,7 +490,58 @@ Dictionary View Objects
always succeeds.


Ordered Dictionaries
Frozen dictionary objects
^^^^^^^^^^^^^^^^^^^^^^^^^

.. versionadded:: next


.. c:var:: PyTypeObject PyFrozenDict_Type

This instance of :c:type:`PyTypeObject` represents the Python frozen
dictionary type.
This is the same object as :class:`frozendict` in the Python layer.


.. c:function:: int PyAnyDict_Check(PyObject *p)

Return true if *p* is a :class:`dict` object, a :class:`frozendict` object,
or an instance of a subtype of the :class:`!dict` or :class:`!frozendict`
type.
This function always succeeds.


.. c:function:: int PyAnyDict_CheckExact(PyObject *p)

Return true if *p* is a :class:`dict` object or a :class:`frozendict` object,
but not an instance of a subtype of the :class:`!dict` or
:class:`!frozendict` type.
This function always succeeds.


.. c:function:: int PyFrozenDict_Check(PyObject *p)

Return true if *p* is a :class:`frozendict` object or an instance of a
subtype of the :class:`!frozendict` type.
This function always succeeds.


.. c:function:: int PyFrozenDict_CheckExact(PyObject *p)

Return true if *p* is a :class:`frozendict` object, but not an instance of a
subtype of the :class:`!frozendict` type.
This function always succeeds.


.. c:function:: PyObject* PyFrozenDict_New(PyObject *iterable)

Return a new :class:`frozendict` from an iterable, or ``NULL`` on failure
with an exception set.

Create an empty dictionary if *iterable* is ``NULL``.


Ordered dictionaries
^^^^^^^^^^^^^^^^^^^^

Python's C API provides interface for :class:`collections.OrderedDict` from C.
Expand Down
52 changes: 44 additions & 8 deletions Doc/library/stdtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5305,8 +5305,8 @@ frozenset, a temporary one is created from *elem*.

.. _typesmapping:

Mapping Types --- :class:`dict`
===============================
Mapping types --- :class:`!dict`, :class:`!frozendict`
======================================================

.. index::
pair: object; mapping
Expand All @@ -5317,8 +5317,9 @@ Mapping Types --- :class:`dict`
pair: built-in function; len

A :term:`mapping` object maps :term:`hashable` values to arbitrary objects.
Mappings are mutable objects. There is currently only one standard mapping
type, the :dfn:`dictionary`. (For other containers see the built-in
There are currently two standard mapping types, the :dfn:`dictionary` and
:class:`frozendict`.
(For other containers see the built-in
:class:`list`, :class:`set`, and :class:`tuple` classes, and the
:mod:`collections` module.)

Expand Down Expand Up @@ -5588,10 +5589,9 @@ can be used interchangeably to index the same dictionary entry.
Dictionaries are now reversible.


.. seealso::
:class:`types.MappingProxyType` can be used to create a read-only view
of a :class:`dict`.

.. seealso::
:class:`types.MappingProxyType` can be used to create a read-only view
of a :class:`dict`.

.. _thread-safety-dict:

Expand Down Expand Up @@ -5839,6 +5839,41 @@ An example of dictionary view usage::
500


Frozen dictionaries
-------------------

.. class:: frozendict(**kwargs)
frozendict(mapping, /, **kwargs)
frozendict(iterable, /, **kwargs)

Return a new frozen dictionary initialized from an optional positional
argument and a possibly empty set of keyword arguments.

A :class:`!frozendict` has a similar API to the :class:`dict` API, with the
following differences:

* :class:`!dict` has more methods than :class:`!frozendict`:

* :meth:`!__delitem__`
* :meth:`!__setitem__`
* :meth:`~dict.clear`
* :meth:`~dict.pop`
* :meth:`~dict.popitem`
* :meth:`~dict.setdefault`
* :meth:`~dict.update`

* A :class:`!frozendict` can be hashed with ``hash(frozendict)`` if all keys and
values can be hashed.

* ``frozendict |= other`` does not modify the :class:`!frozendict` in-place but
creates a new frozen dictionary.

:class:`!frozendict` is not a :class:`!dict` subclass but inherits directly
from ``object``.

.. versionadded:: next


.. _typecontextmanager:

Context Manager Types
Expand Down Expand Up @@ -6062,6 +6097,7 @@ list is non-exhaustive.
* :class:`list`
* :class:`dict`
* :class:`set`
* :class:`frozendict`
* :class:`frozenset`
* :class:`type`
* :class:`asyncio.Future`
Expand Down
27 changes: 27 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Summary -- Release highlights

* :pep:`810`: :ref:`Explicit lazy imports for faster startup times
<whatsnew315-pep810>`
* :pep:`814`: :ref:`Add frozendict built-in type
<whatsnew315-pep814>`
* :pep:`799`: :ref:`A dedicated profiling package for organizing Python
profiling tools <whatsnew315-profiling-package>`
* :pep:`799`: :ref:`Tachyon: High frequency statistical sampling profiler
Expand Down Expand Up @@ -180,6 +182,21 @@ raise :exc:`SyntaxError`).

(Contributed by Pablo Galindo Salgado and Dino Viehland in :gh:`142349`.)


.. _whatsnew315-pep814:

:pep:`814`: Add frozendict built-in type
----------------------------------------

A new public immutable type :class:`frozendict` is added to the :mod:`builtins`
module. It is not a ``dict`` subclass but inherits directly from ``object``.

A ``frozendict`` can be hashed with ``hash(frozendict)`` if all keys and values
can be hashed.

.. seealso:: :pep:`814` for the full specification and rationale.


.. _whatsnew315-profiling-package:

:pep:`799`: A dedicated profiling package
Expand Down Expand Up @@ -1512,6 +1529,16 @@ C API changes
New features
------------

* Add the following functions for the new :class:`frozendict` type:

* :c:func:`PyAnyDict_Check`
* :c:func:`PyAnyDict_CheckExact`
* :c:func:`PyFrozenDict_Check`
* :c:func:`PyFrozenDict_CheckExact`
* :c:func:`PyFrozenDict_New`

(Contributed by Victor Stinner in :gh:`141510`.)

* Add :c:func:`PySys_GetAttr`, :c:func:`PySys_GetAttrString`,
:c:func:`PySys_GetOptionalAttr`, and :c:func:`PySys_GetOptionalAttrString`
functions as replacements for :c:func:`PySys_GetObject`.
Expand Down
15 changes: 14 additions & 1 deletion Include/cpython/dictobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ typedef struct {
PyDictValues *ma_values;
} PyDictObject;

// frozendict
PyAPI_DATA(PyTypeObject) PyFrozenDict_Type;
#define PyFrozenDict_Check(op) PyObject_TypeCheck((op), &PyFrozenDict_Type)
#define PyFrozenDict_CheckExact(op) Py_IS_TYPE((op), &PyFrozenDict_Type)

#define PyAnyDict_CheckExact(ob) \
(PyDict_CheckExact(ob) || PyFrozenDict_CheckExact(ob))
#define PyAnyDict_Check(ob) \
(PyDict_Check(ob) || PyFrozenDict_Check(ob))

PyAPI_FUNC(PyObject *) _PyDict_GetItem_KnownHash(PyObject *mp, PyObject *key,
Py_hash_t hash);
// PyDict_GetItemStringRef() can be used instead
Expand All @@ -42,7 +52,7 @@ PyAPI_FUNC(PyObject *) PyDict_SetDefault(
/* Get the number of items of a dictionary. */
static inline Py_ssize_t PyDict_GET_SIZE(PyObject *op) {
PyDictObject *mp;
assert(PyDict_Check(op));
assert(PyAnyDict_Check(op));
mp = _Py_CAST(PyDictObject*, op);
#ifdef Py_GIL_DISABLED
return _Py_atomic_load_ssize_relaxed(&mp->ma_used);
Expand Down Expand Up @@ -93,3 +103,6 @@ PyAPI_FUNC(int) PyDict_ClearWatcher(int watcher_id);
// Mark given dictionary as "watched" (callback will be called if it is modified)
PyAPI_FUNC(int) PyDict_Watch(int watcher_id, PyObject* dict);
PyAPI_FUNC(int) PyDict_Unwatch(int watcher_id, PyObject* dict);

// Create a frozendict. Create an empty dictionary if iterable is NULL.
PyAPI_FUNC(PyObject*) PyFrozenDict_New(PyObject *iterable);
9 changes: 9 additions & 0 deletions Include/internal/pycore_dict.h
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,15 @@ _Py_DECREF_BUILTINS(PyObject *op)
}
#endif

/* frozendict */
typedef struct {
PyDictObject ob_base;
Py_hash_t ma_hash;
} PyFrozenDictObject;

#define _PyFrozenDictObject_CAST(op) \
(assert(PyFrozenDict_Check(op)), _Py_CAST(PyFrozenDictObject*, (op)))

#ifdef __cplusplus
}
#endif
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_typeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ extern "C" {
#define _Py_TYPE_VERSION_BYTEARRAY 9
#define _Py_TYPE_VERSION_BYTES 10
#define _Py_TYPE_VERSION_COMPLEX 11
#define _Py_TYPE_VERSION_FROZENDICT 12

#define _Py_TYPE_VERSION_NEXT 16

Expand Down
1 change: 1 addition & 0 deletions Lib/_collections_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ def __eq__(self, other):

__reversed__ = None

Mapping.register(frozendict)
Mapping.register(mappingproxy)
Mapping.register(framelocalsproxy)

Expand Down
Loading
Loading