summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/stdtypes.rst12
-rw-r--r--Doc/whatsnew/3.10.rst5
-rw-r--r--Lib/test/test_dict.py20
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2020-06-09-00-20-13.bpo-40890.LoRV-g.rst1
-rw-r--r--Objects/dictobject.c23
5 files changed, 58 insertions, 3 deletions
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index 2082b84..7028d24 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -4622,6 +4622,12 @@ support membership tests:
.. versionchanged:: 3.8
Dictionary views are now reversible.
+.. describe:: dictview.mapping
+
+ Return a :class:`types.MappingProxyType` that wraps the original
+ dictionary to which the view refers.
+
+ .. versionadded:: 3.10
Keys views are set-like since their entries are unique and hashable. If all
values are hashable, so that ``(key, value)`` pairs are unique and hashable,
@@ -4661,6 +4667,12 @@ An example of dictionary view usage::
>>> keys ^ {'sausage', 'juice'}
{'juice', 'sausage', 'bacon', 'spam'}
+ >>> # get back a read-only proxy for the original dictionary
+ >>> values.mapping
+ mappingproxy({'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500})
+ >>> values.mapping['spam']
+ 500
+
.. _typecontextmanager:
diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst
index 1234b2e..629909b 100644
--- a/Doc/whatsnew/3.10.rst
+++ b/Doc/whatsnew/3.10.rst
@@ -74,6 +74,11 @@ New Features
number of ones in the binary expansion of a given integer, also known
as the population count. (Contributed by Niklas Fiekas in :issue:`29882`.)
+* The views returned by :meth:`dict.keys`, :meth:`dict.values` and
+ :meth:`dict.items` now all have a ``mapping`` attribute that gives a
+ :class:`types.MappingProxyType` object wrapping the original
+ dictionary. (Contributed by Dennis Sweeney in :issue:`40890`.)
+
Other Language Changes
======================
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index 5c08810..9ff8b7d 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -105,6 +105,26 @@ class DictTest(unittest.TestCase):
self.assertRaises(TypeError, d.items, None)
self.assertEqual(repr(dict(a=1).items()), "dict_items([('a', 1)])")
+ def test_views_mapping(self):
+ mappingproxy = type(type.__dict__)
+ class Dict(dict):
+ pass
+ for cls in [dict, Dict]:
+ d = cls()
+ m1 = d.keys().mapping
+ m2 = d.values().mapping
+ m3 = d.items().mapping
+
+ for m in [m1, m2, m3]:
+ self.assertIsInstance(m, mappingproxy)
+ self.assertEqual(m, d)
+
+ d["foo"] = "bar"
+
+ for m in [m1, m2, m3]:
+ self.assertIsInstance(m, mappingproxy)
+ self.assertEqual(m, d)
+
def test_contains(self):
d = {}
self.assertNotIn('a', d)
diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-09-00-20-13.bpo-40890.LoRV-g.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-09-00-20-13.bpo-40890.LoRV-g.rst
new file mode 100644
index 0000000..eaefc89
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2020-06-09-00-20-13.bpo-40890.LoRV-g.rst
@@ -0,0 +1 @@
+Each dictionary view now has a ``mapping`` attribute that provides a :class:`types.MappingProxyType` wrapping the original dictionary. Patch contributed by Dennis Sweeney. \ No newline at end of file
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 1bb8cfd..48e96a0 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -4122,6 +4122,23 @@ _PyDictView_New(PyObject *dict, PyTypeObject *type)
return (PyObject *)dv;
}
+static PyObject *
+dictview_mapping(PyObject *view)
+{
+ assert(view != NULL);
+ assert(PyDictKeys_Check(view)
+ || PyDictValues_Check(view)
+ || PyDictItems_Check(view));
+ PyObject *mapping = (PyObject *)((_PyDictViewObject *)view)->dv_dict;
+ return PyDictProxy_New(mapping);
+}
+
+static PyGetSetDef dictview_getset[] = {
+ {"mapping", (getter)dictview_mapping, (setter)NULL,
+ "dictionary that this view refers to", NULL},
+ {0}
+};
+
/* TODO(guido): The views objects are not complete:
* support more set operations
@@ -4635,7 +4652,7 @@ PyTypeObject PyDictKeys_Type = {
(getiterfunc)dictkeys_iter, /* tp_iter */
0, /* tp_iternext */
dictkeys_methods, /* tp_methods */
- 0,
+ .tp_getset = dictview_getset,
};
static PyObject *
@@ -4741,7 +4758,7 @@ PyTypeObject PyDictItems_Type = {
(getiterfunc)dictitems_iter, /* tp_iter */
0, /* tp_iternext */
dictitems_methods, /* tp_methods */
- 0,
+ .tp_getset = dictview_getset,
};
static PyObject *
@@ -4822,7 +4839,7 @@ PyTypeObject PyDictValues_Type = {
(getiterfunc)dictvalues_iter, /* tp_iter */
0, /* tp_iternext */
dictvalues_methods, /* tp_methods */
- 0,
+ .tp_getset = dictview_getset,
};
static PyObject *