]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-40890: Add `mapping` property to dict views (GH-20749)
authorDennis Sweeney <36520290+sweeneyde@users.noreply.github.com>
Fri, 12 Jun 2020 17:19:25 +0000 (13:19 -0400)
committerGitHub <noreply@github.com>
Fri, 12 Jun 2020 17:19:25 +0000 (10:19 -0700)
Doc/library/stdtypes.rst
Doc/whatsnew/3.10.rst
Lib/test/test_dict.py
Misc/NEWS.d/next/Core and Builtins/2020-06-09-00-20-13.bpo-40890.LoRV-g.rst [new file with mode: 0644]
Objects/dictobject.c

index 2082b849fd05b0bbbe7c8ecba084a7205223dff2..7028d240c59ebb428e19809d44ebc7ac77adcc10 100644 (file)
@@ -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:
 
index 1234b2e6bbf279f8e306758effd571fdcf6eb19b..629909b79e2aa04f04e7097cee2d68b67ecae03a 100644 (file)
@@ -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
 ======================
index 5c08810f879b1fd9fce6029c228949a6aae31cd8..9ff8b7d501aad63e59da742facd033e41c7ab1ba 100644 (file)
@@ -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 (file)
index 0000000..eaefc89
--- /dev/null
@@ -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
index 1bb8cfdab2b68144f73d0fa66df06cde58c4e56d..48e96a09a5f87c419c9bafe0223d61b7c1f734de 100644 (file)
@@ -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 *