[Python-checkins] r77428 - in python/trunk: Doc/glossary.rst Doc/library/stdtypes.rst Include/dictobject.h Lib/test/test_dictviews.py Misc/NEWS Objects/dictobject.c

alexandre.vassalotti python-checkins at python.org
Tue Jan 12 00:17:13 CET 2010


Author: alexandre.vassalotti
Date: Tue Jan 12 00:17:10 2010
New Revision: 77428

Log:
Issue #1967: Backport dictionary views.


Added:
   python/trunk/Lib/test/test_dictviews.py
Modified:
   python/trunk/Doc/glossary.rst
   python/trunk/Doc/library/stdtypes.rst
   python/trunk/Include/dictobject.h
   python/trunk/Misc/NEWS
   python/trunk/Objects/dictobject.c

Modified: python/trunk/Doc/glossary.rst
==============================================================================
--- python/trunk/Doc/glossary.rst	(original)
+++ python/trunk/Doc/glossary.rst	Tue Jan 12 00:17:10 2010
@@ -540,6 +540,13 @@
       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.

Modified: python/trunk/Doc/library/stdtypes.rst
==============================================================================
--- python/trunk/Doc/library/stdtypes.rst	(original)
+++ python/trunk/Doc/library/stdtypes.rst	Tue Jan 12 00:17:10 2010
@@ -2106,6 +2106,121 @@
       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:
 

Modified: python/trunk/Include/dictobject.h
==============================================================================
--- python/trunk/Include/dictobject.h	(original)
+++ python/trunk/Include/dictobject.h	Tue Jan 12 00:17:10 2010
@@ -89,10 +89,22 @@
 };
 
 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);

Added: python/trunk/Lib/test/test_dictviews.py
==============================================================================
--- (empty file)
+++ python/trunk/Lib/test/test_dictviews.py	Tue Jan 12 00:17:10 2010
@@ -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()

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Tue Jan 12 00:17:10 2010
@@ -18,6 +18,8 @@
 
 - Issue #2333: Backport set and dict comprehensions syntax from Python 3.x.
 
+- Issue #1967: Backport dictionary views from Python 3.x.
+
 Library
 -------
 

Modified: python/trunk/Objects/dictobject.c
==============================================================================
--- python/trunk/Objects/dictobject.c	(original)
+++ python/trunk/Objects/dictobject.c	Tue Jan 12 00:17:10 2010
@@ -2184,6 +2184,18 @@
 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 @@
 	 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 @@
 	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);
+}


More information about the Python-checkins mailing list