Skip to content

Commit 3ee0e48

Browse files
authored
bpo-40890: Add mapping property to dict views (GH-20749)
1 parent 0d3350d commit 3ee0e48

File tree

5 files changed

+58
-3
lines changed

5 files changed

+58
-3
lines changed

Doc/library/stdtypes.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4622,6 +4622,12 @@ support membership tests:
46224622
.. versionchanged:: 3.8
46234623
Dictionary views are now reversible.
46244624

4625+
.. describe:: dictview.mapping
4626+
4627+
Return a :class:`types.MappingProxyType` that wraps the original
4628+
dictionary to which the view refers.
4629+
4630+
.. versionadded:: 3.10
46254631

46264632
Keys views are set-like since their entries are unique and hashable. If all
46274633
values are hashable, so that ``(key, value)`` pairs are unique and hashable,
@@ -4661,6 +4667,12 @@ An example of dictionary view usage::
46614667
>>> keys ^ {'sausage', 'juice'}
46624668
{'juice', 'sausage', 'bacon', 'spam'}
46634669

4670+
>>> # get back a read-only proxy for the original dictionary
4671+
>>> values.mapping
4672+
mappingproxy({'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500})
4673+
>>> values.mapping['spam']
4674+
500
4675+
46644676

46654677
.. _typecontextmanager:
46664678

Doc/whatsnew/3.10.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ New Features
7474
number of ones in the binary expansion of a given integer, also known
7575
as the population count. (Contributed by Niklas Fiekas in :issue:`29882`.)
7676

77+
* The views returned by :meth:`dict.keys`, :meth:`dict.values` and
78+
:meth:`dict.items` now all have a ``mapping`` attribute that gives a
79+
:class:`types.MappingProxyType` object wrapping the original
80+
dictionary. (Contributed by Dennis Sweeney in :issue:`40890`.)
81+
7782

7883
Other Language Changes
7984
======================

Lib/test/test_dict.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,26 @@ def test_items(self):
105105
self.assertRaises(TypeError, d.items, None)
106106
self.assertEqual(repr(dict(a=1).items()), "dict_items([('a', 1)])")
107107

108+
def test_views_mapping(self):
109+
mappingproxy = type(type.__dict__)
110+
class Dict(dict):
111+
pass
112+
for cls in [dict, Dict]:
113+
d = cls()
114+
m1 = d.keys().mapping
115+
m2 = d.values().mapping
116+
m3 = d.items().mapping
117+
118+
for m in [m1, m2, m3]:
119+
self.assertIsInstance(m, mappingproxy)
120+
self.assertEqual(m, d)
121+
122+
d["foo"] = "bar"
123+
124+
for m in [m1, m2, m3]:
125+
self.assertIsInstance(m, mappingproxy)
126+
self.assertEqual(m, d)
127+
108128
def test_contains(self):
109129
d = {}
110130
self.assertNotIn('a', d)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Each dictionary view now has a ``mapping`` attribute that provides a :class:`types.MappingProxyType` wrapping the original dictionary. Patch contributed by Dennis Sweeney.

Objects/dictobject.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4122,6 +4122,23 @@ _PyDictView_New(PyObject *dict, PyTypeObject *type)
41224122
return (PyObject *)dv;
41234123
}
41244124

4125+
static PyObject *
4126+
dictview_mapping(PyObject *view)
4127+
{
4128+
assert(view != NULL);
4129+
assert(PyDictKeys_Check(view)
4130+
|| PyDictValues_Check(view)
4131+
|| PyDictItems_Check(view));
4132+
PyObject *mapping = (PyObject *)((_PyDictViewObject *)view)->dv_dict;
4133+
return PyDictProxy_New(mapping);
4134+
}
4135+
4136+
static PyGetSetDef dictview_getset[] = {
4137+
{"mapping", (getter)dictview_mapping, (setter)NULL,
4138+
"dictionary that this view refers to", NULL},
4139+
{0}
4140+
};
4141+
41254142
/* TODO(guido): The views objects are not complete:
41264143
41274144
* support more set operations
@@ -4635,7 +4652,7 @@ PyTypeObject PyDictKeys_Type = {
46354652
(getiterfunc)dictkeys_iter, /* tp_iter */
46364653
0, /* tp_iternext */
46374654
dictkeys_methods, /* tp_methods */
4638-
0,
4655+
.tp_getset = dictview_getset,
46394656
};
46404657

46414658
static PyObject *
@@ -4741,7 +4758,7 @@ PyTypeObject PyDictItems_Type = {
47414758
(getiterfunc)dictitems_iter, /* tp_iter */
47424759
0, /* tp_iternext */
47434760
dictitems_methods, /* tp_methods */
4744-
0,
4761+
.tp_getset = dictview_getset,
47454762
};
47464763

47474764
static PyObject *
@@ -4822,7 +4839,7 @@ PyTypeObject PyDictValues_Type = {
48224839
(getiterfunc)dictvalues_iter, /* tp_iter */
48234840
0, /* tp_iternext */
48244841
dictvalues_methods, /* tp_methods */
4825-
0,
4842+
.tp_getset = dictview_getset,
48264843
};
48274844

48284845
static PyObject *

0 commit comments

Comments
 (0)