summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandre Vassalotti <alexandre@peadrop.com>2010-01-11 23:17:10 (GMT)
committerAlexandre Vassalotti <alexandre@peadrop.com>2010-01-11 23:17:10 (GMT)
commit69eb51697c65f8a1009e51f80f3d7abcc62b7344 (patch)
tree4dba5e0e40d26847db55be34617db0442d50fba2
parent7a8df802851ae997d65a5022224eddfa3843e06e (diff)
downloadcpython-69eb51697c65f8a1009e51f80f3d7abcc62b7344.zip
cpython-69eb51697c65f8a1009e51f80f3d7abcc62b7344.tar.gz
cpython-69eb51697c65f8a1009e51f80f3d7abcc62b7344.tar.bz2
Issue #1967: Backport dictionary views.
-rw-r--r--Doc/glossary.rst7
-rw-r--r--Doc/library/stdtypes.rst115
-rw-r--r--Include/dictobject.h12
-rw-r--r--Lib/test/test_dictviews.py84
-rw-r--r--Misc/NEWS2
-rw-r--r--Objects/dictobject.c505
6 files changed, 725 insertions, 0 deletions
diff --git a/Doc/glossary.rst b/Doc/glossary.rst
index f7689af..22c1372 100644
--- a/Doc/glossary.rst
+++ b/Doc/glossary.rst
@@ -540,6 +540,13 @@ Glossary
object has a type. An object's type is accessible as its
:attr:`__class__` attribute or can be retrieved with ``type(obj)``.
+ view
+ The objects returned from :meth:`dict.viewkeys`, :meth:`dict.viewvalues`,
+ and :meth:`dict.viewitems` are called dictionary views. They are lazy
+ sequences that will see changes in the underlying dictionary. To force
+ the dictionary view to become a full list use ``list(dictview)``. See
+ :ref:`dict-views`.
+
virtual machine
A computer defined entirely in software. Python's virtual machine
executes the :term:`bytecode` emitted by the bytecode compiler.
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index a35a8ad..1a1ed46 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -2106,6 +2106,121 @@ pairs within braces, for example: ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098:
Return a copy of the dictionary's list of values. See the note for
:meth:`dict.items`.
+ .. method:: viewitems()
+
+ Return a new view of the dictionary's items (``(key, value)`` pairs). See
+ below for documentation of view objects.
+
+ .. versionadded:: 2.7
+
+ .. method:: viewkeys()
+
+ Return a new view of the dictionary's keys. See below for documentation of
+ view objects.
+
+ .. versionadded:: 2.7
+
+ .. method:: viewvalues()
+
+ Return a new view of the dictionary's values. See below for documentation of
+ view objects.
+
+ .. versionadded:: 2.7
+
+
+.. _dict-views:
+
+Dictionary view objects
+-----------------------
+
+The objects returned by :meth:`dict.viewkeys`, :meth:`dict.viewvalues` and
+:meth:`dict.viewitems` are *view objects*. They provide a dynamic view on the
+dictionary's entries, which means that when the dictionary changes, the view
+reflects these changes.
+
+Dictionary views can be iterated over to yield their respective data, and
+support membership tests:
+
+.. describe:: len(dictview)
+
+ Return the number of entries in the dictionary.
+
+.. describe:: iter(dictview)
+
+ Return an iterator over the keys, values or items (represented as tuples of
+ ``(key, value)``) in the dictionary.
+
+ Keys and values are iterated over in an arbitrary order which is non-random,
+ varies across Python implementations, and depends on the dictionary's history
+ of insertions and deletions. If keys, values and items views are iterated
+ over with no intervening modifications to the dictionary, the order of items
+ will directly correspond. This allows the creation of ``(value, key)`` pairs
+ using :func:`zip`: ``pairs = zip(d.values(), d.keys())``. Another way to
+ create the same list is ``pairs = [(v, k) for (k, v) in d.items()]``.
+
+ Iterating views while adding or deleting entries in the dictionary may raise
+ a :exc:`RuntimeError` or fail to iterate over all entries.
+
+.. describe:: x in dictview
+
+ Return ``True`` if *x* is in the underlying dictionary's keys, values or
+ items (in the latter case, *x* should be a ``(key, value)`` tuple).
+
+
+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, then
+the items view is also set-like. (Values views are not treated as set-like
+since the entries are generally not unique.) Then these set operations are
+available ("other" refers either to another view or a set):
+
+.. describe:: dictview & other
+
+ Return the intersection of the dictview and the other object as a new set.
+
+.. describe:: dictview | other
+
+ Return the union of the dictview and the other object as a new set.
+
+.. describe:: dictview - other
+
+ Return the difference between the dictview and the other object (all elements
+ in *dictview* that aren't in *other*) as a new set.
+
+.. describe:: dictview ^ other
+
+ Return the symmetric difference (all elements either in *dictview* or
+ *other*, but not in both) of the dictview and the other object as a new set.
+
+
+An example of dictionary view usage::
+
+ >>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500}
+ >>> keys = dishes.viewkeys()
+ >>> values = dishes.viewvalues()
+
+ >>> # iteration
+ >>> n = 0
+ >>> for val in values:
+ ... n += val
+ >>> print(n)
+ 504
+
+ >>> # keys and values are iterated over in the same order
+ >>> list(keys)
+ ['eggs', 'bacon', 'sausage', 'spam']
+ >>> list(values)
+ [2, 1, 1, 500]
+
+ >>> # view objects are dynamic and reflect dict changes
+ >>> del dishes['eggs']
+ >>> del dishes['sausage']
+ >>> list(keys)
+ ['spam', 'bacon']
+
+ >>> # set operations
+ >>> keys & {'eggs', 'bacon', 'salad'}
+ {'bacon'}
+
.. _bltin-file-objects:
diff --git a/Include/dictobject.h b/Include/dictobject.h
index 06e0a7e..dfd42dc 100644
--- a/Include/dictobject.h
+++ b/Include/dictobject.h
@@ -89,10 +89,22 @@ struct _dictobject {
};
PyAPI_DATA(PyTypeObject) PyDict_Type;
+PyAPI_DATA(PyTypeObject) PyDictIterKey_Type;
+PyAPI_DATA(PyTypeObject) PyDictIterValue_Type;
+PyAPI_DATA(PyTypeObject) PyDictIterItem_Type;
+PyAPI_DATA(PyTypeObject) PyDictKeys_Type;
+PyAPI_DATA(PyTypeObject) PyDictItems_Type;
+PyAPI_DATA(PyTypeObject) PyDictValues_Type;
#define PyDict_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_DICT_SUBCLASS)
#define PyDict_CheckExact(op) (Py_TYPE(op) == &PyDict_Type)
+#define PyDictKeys_Check(op) (Py_TYPE(op) == &PyDictKeys_Type)
+#define PyDictItems_Check(op) (Py_TYPE(op) == &PyDictItems_Type)
+#define PyDictValues_Check(op) (Py_TYPE(op) == &PyDictValues_Type)
+/* This excludes Values, since they are not sets. */
+# define PyDictViewSet_Check(op) \
+ (PyDictKeys_Check(op) || PyDictItems_Check(op))
PyAPI_FUNC(PyObject *) PyDict_New(void);
PyAPI_FUNC(PyObject *) PyDict_GetItem(PyObject *mp, PyObject *key);
diff --git a/Lib/test/test_dictviews.py b/Lib/test/test_dictviews.py
new file mode 100644
index 0000000..8821139
--- /dev/null
+++ b/Lib/test/test_dictviews.py
@@ -0,0 +1,84 @@
+import unittest
+from test import test_support
+
+class DictSetTest(unittest.TestCase):
+
+ def test_constructors_not_callable(self):
+ kt = type({}.viewkeys())
+ self.assertRaises(TypeError, kt, {})
+ self.assertRaises(TypeError, kt)
+ it = type({}.viewitems())
+ self.assertRaises(TypeError, it, {})
+ self.assertRaises(TypeError, it)
+ vt = type({}.viewvalues())
+ self.assertRaises(TypeError, vt, {})
+ self.assertRaises(TypeError, vt)
+
+ def test_dict_keys(self):
+ d = {1: 10, "a": "ABC"}
+ keys = d.viewkeys()
+ self.assertEqual(len(keys), 2)
+ self.assertEqual(set(keys), set([1, "a"]))
+ self.assertEqual(keys, set([1, "a"]))
+ self.assertNotEqual(keys, set([1, "a", "b"]))
+ self.assertNotEqual(keys, set([1, "b"]))
+ self.assertNotEqual(keys, set([1]))
+ self.assertNotEqual(keys, 42)
+ self.assert_(1 in keys)
+ self.assert_("a" in keys)
+ self.assert_(10 not in keys)
+ self.assert_("Z" not in keys)
+ self.assertEqual(d.viewkeys(), d.viewkeys())
+ e = {1: 11, "a": "def"}
+ self.assertEqual(d.viewkeys(), e.viewkeys())
+ del e["a"]
+ self.assertNotEqual(d.viewkeys(), e.viewkeys())
+
+ def test_dict_items(self):
+ d = {1: 10, "a": "ABC"}
+ items = d.viewitems()
+ self.assertEqual(len(items), 2)
+ self.assertEqual(set(items), set([(1, 10), ("a", "ABC")]))
+ self.assertEqual(items, set([(1, 10), ("a", "ABC")]))
+ self.assertNotEqual(items, set([(1, 10), ("a", "ABC"), "junk"]))
+ self.assertNotEqual(items, set([(1, 10), ("a", "def")]))
+ self.assertNotEqual(items, set([(1, 10)]))
+ self.assertNotEqual(items, 42)
+ self.assert_((1, 10) in items)
+ self.assert_(("a", "ABC") in items)
+ self.assert_((1, 11) not in items)
+ self.assert_(1 not in items)
+ self.assert_(() not in items)
+ self.assert_((1,) not in items)
+ self.assert_((1, 2, 3) not in items)
+ self.assertEqual(d.viewitems(), d.viewitems())
+ e = d.copy()
+ self.assertEqual(d.viewitems(), e.viewitems())
+ e["a"] = "def"
+ self.assertNotEqual(d.viewitems(), e.viewitems())
+
+ def test_dict_mixed_keys_items(self):
+ d = {(1, 1): 11, (2, 2): 22}
+ e = {1: 1, 2: 2}
+ self.assertEqual(d.viewkeys(), e.viewitems())
+ self.assertNotEqual(d.viewitems(), e.viewkeys())
+
+ def test_dict_values(self):
+ d = {1: 10, "a": "ABC"}
+ values = d.viewvalues()
+ self.assertEqual(set(values), set([10, "ABC"]))
+ self.assertEqual(len(values), 2)
+
+ def test_dict_repr(self):
+ d = {1: 10, "a": "ABC"}
+ self.assertTrue(isinstance(repr(d), str))
+ self.assertTrue(isinstance(repr(d.viewitems()), str))
+ self.assertTrue(isinstance(repr(d.viewkeys()), str))
+ self.assertTrue(isinstance(repr(d.viewvalues()), str))
+
+
+def test_main():
+ test_support.run_unittest(DictSetTest)
+
+if __name__ == "__main__":
+ test_main()
diff --git a/Misc/NEWS b/Misc/NEWS
index 3511fe9..19ca01c 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -18,6 +18,8 @@ Core and Builtins
- Issue #2333: Backport set and dict comprehensions syntax from Python 3.x.
+- Issue #1967: Backport dictionary views from Python 3.x.
+
Library
-------
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index af37a93..9f46dfa 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -2184,6 +2184,18 @@ PyDoc_STRVAR(itervalues__doc__,
PyDoc_STRVAR(iteritems__doc__,
"D.iteritems() -> an iterator over the (key, value) items of D");
+/* Forward */
+static PyObject *dictkeys_new(PyObject *);
+static PyObject *dictitems_new(PyObject *);
+static PyObject *dictvalues_new(PyObject *);
+
+PyDoc_STRVAR(viewkeys__doc__,
+ "D.viewkeys() -> a set-like object providing a view on D's keys");
+PyDoc_STRVAR(viewitems__doc__,
+ "D.viewitems() -> a set-like object providing a view on D's items");
+PyDoc_STRVAR(viewvalues__doc__,
+ "D.viewvalues() -> an object providing a view on D's values");
+
static PyMethodDef mapp_methods[] = {
{"__contains__",(PyCFunction)dict_contains, METH_O | METH_COEXIST,
contains__doc__},
@@ -2207,6 +2219,12 @@ static PyMethodDef mapp_methods[] = {
items__doc__},
{"values", (PyCFunction)dict_values, METH_NOARGS,
values__doc__},
+ {"viewkeys", (PyCFunction)dictkeys_new, METH_NOARGS,
+ viewkeys__doc__},
+ {"viewitems", (PyCFunction)dictitems_new, METH_NOARGS,
+ viewitems__doc__},
+ {"viewvalues", (PyCFunction)dictvalues_new, METH_NOARGS,
+ viewvalues__doc__},
{"update", (PyCFunction)dict_update, METH_VARARGS | METH_KEYWORDS,
update__doc__},
{"fromkeys", (PyCFunction)dict_fromkeys, METH_VARARGS | METH_CLASS,
@@ -2700,3 +2718,490 @@ PyTypeObject PyDictIterItem_Type = {
dictiter_methods, /* tp_methods */
0,
};
+
+/***********************************************/
+/* View objects for keys(), items(), values(). */
+/***********************************************/
+
+/* The instance lay-out is the same for all three; but the type differs. */
+
+typedef struct {
+ PyObject_HEAD
+ PyDictObject *dv_dict;
+} dictviewobject;
+
+
+static void
+dictview_dealloc(dictviewobject *dv)
+{
+ Py_XDECREF(dv->dv_dict);
+ PyObject_GC_Del(dv);
+}
+
+static int
+dictview_traverse(dictviewobject *dv, visitproc visit, void *arg)
+{
+ Py_VISIT(dv->dv_dict);
+ return 0;
+}
+
+static Py_ssize_t
+dictview_len(dictviewobject *dv)
+{
+ Py_ssize_t len = 0;
+ if (dv->dv_dict != NULL)
+ len = dv->dv_dict->ma_used;
+ return len;
+}
+
+static PyObject *
+dictview_new(PyObject *dict, PyTypeObject *type)
+{
+ dictviewobject *dv;
+ if (dict == NULL) {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+ if (!PyDict_Check(dict)) {
+ /* XXX Get rid of this restriction later */
+ PyErr_Format(PyExc_TypeError,
+ "%s() requires a dict argument, not '%s'",
+ type->tp_name, dict->ob_type->tp_name);
+ return NULL;
+ }
+ dv = PyObject_GC_New(dictviewobject, type);
+ if (dv == NULL)
+ return NULL;
+ Py_INCREF(dict);
+ dv->dv_dict = (PyDictObject *)dict;
+ _PyObject_GC_TRACK(dv);
+ return (PyObject *)dv;
+}
+
+/* TODO(guido): The views objects are not complete:
+
+ * support more set operations
+ * support arbitrary mappings?
+ - either these should be static or exported in dictobject.h
+ - if public then they should probably be in builtins
+*/
+
+/* Return 1 if self is a subset of other, iterating over self;
+ 0 if not; -1 if an error occurred. */
+static int
+all_contained_in(PyObject *self, PyObject *other)
+{
+ PyObject *iter = PyObject_GetIter(self);
+ int ok = 1;
+
+ if (iter == NULL)
+ return -1;
+ for (;;) {
+ PyObject *next = PyIter_Next(iter);
+ if (next == NULL) {
+ if (PyErr_Occurred())
+ ok = -1;
+ break;
+ }
+ ok = PySequence_Contains(other, next);
+ Py_DECREF(next);
+ if (ok <= 0)
+ break;
+ }
+ Py_DECREF(iter);
+ return ok;
+}
+
+static PyObject *
+dictview_richcompare(PyObject *self, PyObject *other, int op)
+{
+ Py_ssize_t len_self, len_other;
+ int ok;
+ PyObject *result;
+
+ assert(self != NULL);
+ assert(PyDictViewSet_Check(self));
+ assert(other != NULL);
+
+ if (!PyAnySet_Check(other) && !PyDictViewSet_Check(other)) {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+
+ len_self = PyObject_Size(self);
+ if (len_self < 0)
+ return NULL;
+ len_other = PyObject_Size(other);
+ if (len_other < 0)
+ return NULL;
+
+ ok = 0;
+ switch(op) {
+
+ case Py_NE:
+ case Py_EQ:
+ if (len_self == len_other)
+ ok = all_contained_in(self, other);
+ if (op == Py_NE && ok >= 0)
+ ok = !ok;
+ break;
+
+ case Py_LT:
+ if (len_self < len_other)
+ ok = all_contained_in(self, other);
+ break;
+
+ case Py_LE:
+ if (len_self <= len_other)
+ ok = all_contained_in(self, other);
+ break;
+
+ case Py_GT:
+ if (len_self > len_other)
+ ok = all_contained_in(other, self);
+ break;
+
+ case Py_GE:
+ if (len_self >= len_other)
+ ok = all_contained_in(other, self);
+ break;
+
+ }
+ if (ok < 0)
+ return NULL;
+ result = ok ? Py_True : Py_False;
+ Py_INCREF(result);
+ return result;
+}
+
+static PyObject *
+dictview_repr(dictviewobject *dv)
+{
+ PyObject *seq;
+ PyObject *seq_str;
+ PyObject *result;
+
+ seq = PySequence_List((PyObject *)dv);
+ if (seq == NULL)
+ return NULL;
+
+ seq_str = PyObject_Repr(seq);
+ result = PyString_FromFormat("%s(%s)", Py_TYPE(dv)->tp_name, seq_str);
+ Py_DECREF(seq_str);
+ Py_DECREF(seq);
+ return result;
+}
+
+/*** dict_keys ***/
+
+static PyObject *
+dictkeys_iter(dictviewobject *dv)
+{
+ if (dv->dv_dict == NULL) {
+ Py_RETURN_NONE;
+ }
+ return dictiter_new(dv->dv_dict, &PyDictIterKey_Type);
+}
+
+static int
+dictkeys_contains(dictviewobject *dv, PyObject *obj)
+{
+ if (dv->dv_dict == NULL)
+ return 0;
+ return PyDict_Contains((PyObject *)dv->dv_dict, obj);
+}
+
+static PySequenceMethods dictkeys_as_sequence = {
+ (lenfunc)dictview_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)dictkeys_contains, /* sq_contains */
+};
+
+static PyObject*
+dictviews_sub(PyObject* self, PyObject *other)
+{
+ PyObject *result = PySet_New(self);
+ PyObject *tmp;
+ if (result == NULL)
+ return NULL;
+
+ tmp = PyObject_CallMethod(result, "difference_update", "O", other);
+ if (tmp == NULL) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ Py_DECREF(tmp);
+ return result;
+}
+
+static PyObject*
+dictviews_and(PyObject* self, PyObject *other)
+{
+ PyObject *result = PySet_New(self);
+ PyObject *tmp;
+ if (result == NULL)
+ return NULL;
+
+ tmp = PyObject_CallMethod(result, "intersection_update", "O", other);
+ if (tmp == NULL) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ Py_DECREF(tmp);
+ return result;
+}
+
+static PyObject*
+dictviews_or(PyObject* self, PyObject *other)
+{
+ PyObject *result = PySet_New(self);
+ PyObject *tmp;
+ if (result == NULL)
+ return NULL;
+
+ tmp = PyObject_CallMethod(result, "update", "O", other);
+ if (tmp == NULL) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ Py_DECREF(tmp);
+ return result;
+}
+
+static PyObject*
+dictviews_xor(PyObject* self, PyObject *other)
+{
+ PyObject *result = PySet_New(self);
+ PyObject *tmp;
+ if (result == NULL)
+ return NULL;
+
+ tmp = PyObject_CallMethod(result, "symmetric_difference_update", "O",
+ other);
+ if (tmp == NULL) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ Py_DECREF(tmp);
+ return result;
+}
+
+static PyNumberMethods dictviews_as_number = {
+ 0, /*nb_add*/
+ (binaryfunc)dictviews_sub, /*nb_subtract*/
+ 0, /*nb_multiply*/
+ 0, /*nb_remainder*/
+ 0, /*nb_divmod*/
+ 0, /*nb_power*/
+ 0, /*nb_negative*/
+ 0, /*nb_positive*/
+ 0, /*nb_absolute*/
+ 0, /*nb_bool*/
+ 0, /*nb_invert*/
+ 0, /*nb_lshift*/
+ 0, /*nb_rshift*/
+ (binaryfunc)dictviews_and, /*nb_and*/
+ (binaryfunc)dictviews_xor, /*nb_xor*/
+ (binaryfunc)dictviews_or, /*nb_or*/
+};
+
+static PyMethodDef dictkeys_methods[] = {
+ {NULL, NULL} /* sentinel */
+};
+
+PyTypeObject PyDictKeys_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "dict_keys", /* tp_name */
+ sizeof(dictviewobject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)dictview_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_reserved */
+ (reprfunc)dictview_repr, /* tp_repr */
+ &dictviews_as_number, /* tp_as_number */
+ &dictkeys_as_sequence, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
+ 0, /* tp_doc */
+ (traverseproc)dictview_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+ dictview_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)dictkeys_iter, /* tp_iter */
+ 0, /* tp_iternext */
+ dictkeys_methods, /* tp_methods */
+ 0,
+};
+
+static PyObject *
+dictkeys_new(PyObject *dict)
+{
+ return dictview_new(dict, &PyDictKeys_Type);
+}
+
+/*** dict_items ***/
+
+static PyObject *
+dictitems_iter(dictviewobject *dv)
+{
+ if (dv->dv_dict == NULL) {
+ Py_RETURN_NONE;
+ }
+ return dictiter_new(dv->dv_dict, &PyDictIterItem_Type);
+}
+
+static int
+dictitems_contains(dictviewobject *dv, PyObject *obj)
+{
+ PyObject *key, *value, *found;
+ if (dv->dv_dict == NULL)
+ return 0;
+ if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2)
+ return 0;
+ key = PyTuple_GET_ITEM(obj, 0);
+ value = PyTuple_GET_ITEM(obj, 1);
+ found = PyDict_GetItem((PyObject *)dv->dv_dict, key);
+ if (found == NULL) {
+ if (PyErr_Occurred())
+ return -1;
+ return 0;
+ }
+ return PyObject_RichCompareBool(value, found, Py_EQ);
+}
+
+static PySequenceMethods dictitems_as_sequence = {
+ (lenfunc)dictview_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)dictitems_contains, /* sq_contains */
+};
+
+static PyMethodDef dictitems_methods[] = {
+ {NULL, NULL} /* sentinel */
+};
+
+PyTypeObject PyDictItems_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "dict_items", /* tp_name */
+ sizeof(dictviewobject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)dictview_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_reserved */
+ (reprfunc)dictview_repr, /* tp_repr */
+ &dictviews_as_number, /* tp_as_number */
+ &dictitems_as_sequence, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
+ 0, /* tp_doc */
+ (traverseproc)dictview_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+ dictview_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)dictitems_iter, /* tp_iter */
+ 0, /* tp_iternext */
+ dictitems_methods, /* tp_methods */
+ 0,
+};
+
+static PyObject *
+dictitems_new(PyObject *dict)
+{
+ return dictview_new(dict, &PyDictItems_Type);
+}
+
+/*** dict_values ***/
+
+static PyObject *
+dictvalues_iter(dictviewobject *dv)
+{
+ if (dv->dv_dict == NULL) {
+ Py_RETURN_NONE;
+ }
+ return dictiter_new(dv->dv_dict, &PyDictIterValue_Type);
+}
+
+static PySequenceMethods dictvalues_as_sequence = {
+ (lenfunc)dictview_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)0, /* sq_contains */
+};
+
+static PyMethodDef dictvalues_methods[] = {
+ {NULL, NULL} /* sentinel */
+};
+
+PyTypeObject PyDictValues_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "dict_values", /* tp_name */
+ sizeof(dictviewobject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)dictview_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_reserved */
+ (reprfunc)dictview_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ &dictvalues_as_sequence, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
+ 0, /* tp_doc */
+ (traverseproc)dictview_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)dictvalues_iter, /* tp_iter */
+ 0, /* tp_iternext */
+ dictvalues_methods, /* tp_methods */
+ 0,
+};
+
+static PyObject *
+dictvalues_new(PyObject *dict)
+{
+ return dictview_new(dict, &PyDictValues_Type);
+}