mirror of https://github.com/python/cpython
Issue #1967: Backport dictionary views.
This commit is contained in:
parent
7a8df80285
commit
69eb51697c
|
@ -540,6 +540,13 @@ Glossary
|
||||||
object has a type. An object's type is accessible as its
|
object has a type. An object's type is accessible as its
|
||||||
:attr:`__class__` attribute or can be retrieved with ``type(obj)``.
|
: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
|
virtual machine
|
||||||
A computer defined entirely in software. Python's virtual machine
|
A computer defined entirely in software. Python's virtual machine
|
||||||
executes the :term:`bytecode` emitted by the bytecode compiler.
|
executes the :term:`bytecode` emitted by the bytecode compiler.
|
||||||
|
|
|
@ -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
|
Return a copy of the dictionary's list of values. See the note for
|
||||||
:meth:`dict.items`.
|
: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:
|
.. _bltin-file-objects:
|
||||||
|
|
||||||
|
|
|
@ -89,10 +89,22 @@ struct _dictobject {
|
||||||
};
|
};
|
||||||
|
|
||||||
PyAPI_DATA(PyTypeObject) PyDict_Type;
|
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) \
|
#define PyDict_Check(op) \
|
||||||
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_DICT_SUBCLASS)
|
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_DICT_SUBCLASS)
|
||||||
#define PyDict_CheckExact(op) (Py_TYPE(op) == &PyDict_Type)
|
#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_New(void);
|
||||||
PyAPI_FUNC(PyObject *) PyDict_GetItem(PyObject *mp, PyObject *key);
|
PyAPI_FUNC(PyObject *) PyDict_GetItem(PyObject *mp, PyObject *key);
|
||||||
|
|
|
@ -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()
|
|
@ -18,6 +18,8 @@ Core and Builtins
|
||||||
|
|
||||||
- Issue #2333: Backport set and dict comprehensions syntax from Python 3.x.
|
- Issue #2333: Backport set and dict comprehensions syntax from Python 3.x.
|
||||||
|
|
||||||
|
- Issue #1967: Backport dictionary views from Python 3.x.
|
||||||
|
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
@ -2184,6 +2184,18 @@ PyDoc_STRVAR(itervalues__doc__,
|
||||||
PyDoc_STRVAR(iteritems__doc__,
|
PyDoc_STRVAR(iteritems__doc__,
|
||||||
"D.iteritems() -> an iterator over the (key, value) items of D");
|
"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[] = {
|
static PyMethodDef mapp_methods[] = {
|
||||||
{"__contains__",(PyCFunction)dict_contains, METH_O | METH_COEXIST,
|
{"__contains__",(PyCFunction)dict_contains, METH_O | METH_COEXIST,
|
||||||
contains__doc__},
|
contains__doc__},
|
||||||
|
@ -2207,6 +2219,12 @@ static PyMethodDef mapp_methods[] = {
|
||||||
items__doc__},
|
items__doc__},
|
||||||
{"values", (PyCFunction)dict_values, METH_NOARGS,
|
{"values", (PyCFunction)dict_values, METH_NOARGS,
|
||||||
values__doc__},
|
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", (PyCFunction)dict_update, METH_VARARGS | METH_KEYWORDS,
|
||||||
update__doc__},
|
update__doc__},
|
||||||
{"fromkeys", (PyCFunction)dict_fromkeys, METH_VARARGS | METH_CLASS,
|
{"fromkeys", (PyCFunction)dict_fromkeys, METH_VARARGS | METH_CLASS,
|
||||||
|
@ -2700,3 +2718,490 @@ PyTypeObject PyDictIterItem_Type = {
|
||||||
dictiter_methods, /* tp_methods */
|
dictiter_methods, /* tp_methods */
|
||||||
0,
|
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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue