Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 07d8112

Browse files
authored
bpo-40889: Optimize dict.items() ^ dict.items() (GH-20718)
1 parent bae872f commit 07d8112

3 files changed

Lines changed: 101 additions & 0 deletions

File tree

Lib/test/test_dict.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,16 @@ def test_dictview_set_operations_on_items(self):
697697
self.assertEqual(k1 ^ k2, {(3,3)})
698698
self.assertEqual(k1 ^ k3, {(1,1), (2,2), (4,4)})
699699

700+
def test_items_symmetric_difference(self):
701+
rr = random.randrange
702+
for _ in range(100):
703+
left = {x:rr(3) for x in range(20) if rr(2)}
704+
right = {x:rr(3) for x in range(20) if rr(2)}
705+
with self.subTest(left=left, right=right):
706+
expected = set(left.items()) ^ set(right.items())
707+
actual = left.items() ^ right.items()
708+
self.assertEqual(actual, expected)
709+
700710
def test_dictview_mixed_set_operations(self):
701711
# Just a few for .keys()
702712
self.assertTrue({1:1}.keys() == {1})
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improved the performance of symmetric difference operations on dictionary item views. Patch by Dennis Sweeney.

Objects/dictobject.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4409,9 +4409,99 @@ dictviews_or(PyObject* self, PyObject *other)
44094409
return result;
44104410
}
44114411

4412+
static PyObject *
4413+
dictitems_xor(PyObject *self, PyObject *other)
4414+
{
4415+
assert(PyDictItems_Check(self));
4416+
assert(PyDictItems_Check(other));
4417+
PyObject *d1 = (PyObject *)((_PyDictViewObject *)self)->dv_dict;
4418+
PyObject *d2 = (PyObject *)((_PyDictViewObject *)other)->dv_dict;
4419+
4420+
PyObject *temp_dict = PyDict_Copy(d1);
4421+
if (temp_dict == NULL) {
4422+
return NULL;
4423+
}
4424+
PyObject *result_set = PySet_New(NULL);
4425+
if (result_set == NULL) {
4426+
Py_CLEAR(temp_dict);
4427+
return NULL;
4428+
}
4429+
4430+
PyObject *key = NULL, *val1 = NULL, *val2 = NULL;
4431+
Py_ssize_t pos = 0;
4432+
Py_hash_t hash;
4433+
4434+
while (_PyDict_Next(d2, &pos, &key, &val2, &hash)) {
4435+
Py_INCREF(key);
4436+
Py_INCREF(val2);
4437+
val1 = _PyDict_GetItem_KnownHash(temp_dict, key, hash);
4438+
4439+
int to_delete;
4440+
if (val1 == NULL) {
4441+
if (PyErr_Occurred()) {
4442+
goto error;
4443+
}
4444+
to_delete = 0;
4445+
}
4446+
else {
4447+
Py_INCREF(val1);
4448+
to_delete = PyObject_RichCompareBool(val1, val2, Py_EQ);
4449+
if (to_delete < 0) {
4450+
goto error;
4451+
}
4452+
}
4453+
4454+
if (to_delete) {
4455+
if (_PyDict_DelItem_KnownHash(temp_dict, key, hash) < 0) {
4456+
goto error;
4457+
}
4458+
}
4459+
else {
4460+
PyObject *pair = PyTuple_Pack(2, key, val2);
4461+
if (pair == NULL) {
4462+
goto error;
4463+
}
4464+
if (PySet_Add(result_set, pair) < 0) {
4465+
Py_DECREF(pair);
4466+
goto error;
4467+
}
4468+
Py_DECREF(pair);
4469+
}
4470+
Py_DECREF(key);
4471+
Py_XDECREF(val1);
4472+
Py_DECREF(val2);
4473+
}
4474+
key = val1 = val2 = NULL;
4475+
4476+
_Py_IDENTIFIER(items);
4477+
PyObject *remaining_pairs = _PyObject_CallMethodIdNoArgs(temp_dict,
4478+
&PyId_items);
4479+
if (remaining_pairs == NULL) {
4480+
goto error;
4481+
}
4482+
if (_PySet_Update(result_set, remaining_pairs) < 0) {
4483+
Py_DECREF(remaining_pairs);
4484+
goto error;
4485+
}
4486+
Py_DECREF(temp_dict);
4487+
Py_DECREF(remaining_pairs);
4488+
return result_set;
4489+
4490+
error:
4491+
Py_XDECREF(temp_dict);
4492+
Py_XDECREF(result_set);
4493+
Py_XDECREF(key);
4494+
Py_XDECREF(val1);
4495+
Py_XDECREF(val2);
4496+
return NULL;
4497+
}
4498+
44124499
static PyObject*
44134500
dictviews_xor(PyObject* self, PyObject *other)
44144501
{
4502+
if (PyDictItems_Check(self) && PyDictItems_Check(other)) {
4503+
return dictitems_xor(self, other);
4504+
}
44154505
PyObject *result = dictviews_to_set(self);
44164506
if (result == NULL) {
44174507
return NULL;

0 commit comments

Comments
 (0)