Skip to content

Commit 6cbc84f

Browse files
authored
bpo-38613: Optimize set operations of dict keys. (GH-16961)
1 parent d12d0e7 commit 6cbc84f

File tree

2 files changed

+36
-21
lines changed

2 files changed

+36
-21
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Optimized some set operations (e.g. ``|``, ``^``, and ``-``) of
2+
``dict_keys``. ``d.keys() | other`` was slower than ``set(d) | other`` but
3+
they are almost same performance for now.

Objects/dictobject.c

+33-21
Original file line numberDiff line numberDiff line change
@@ -4162,17 +4162,34 @@ static PySequenceMethods dictkeys_as_sequence = {
41624162
(objobjproc)dictkeys_contains, /* sq_contains */
41634163
};
41644164

4165+
// Create an set object from dictviews object.
4166+
// Returns a new reference.
4167+
// This utility function is used by set operations.
41654168
static PyObject*
4166-
dictviews_sub(PyObject* self, PyObject *other)
4169+
dictviews_to_set(PyObject *self)
41674170
{
4168-
PyObject *result = PySet_New(self);
4169-
PyObject *tmp;
4170-
_Py_IDENTIFIER(difference_update);
4171+
PyObject *left = self;
4172+
if (PyDictKeys_Check(self)) {
4173+
// PySet_New() has fast path for the dict object.
4174+
PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict;
4175+
if (PyDict_CheckExact(dict)) {
4176+
left = dict;
4177+
}
4178+
}
4179+
return PySet_New(left);
4180+
}
41714181

4172-
if (result == NULL)
4182+
static PyObject*
4183+
dictviews_sub(PyObject *self, PyObject *other)
4184+
{
4185+
PyObject *result = dictviews_to_set(self);
4186+
if (result == NULL) {
41734187
return NULL;
4188+
}
41744189

4175-
tmp = _PyObject_CallMethodIdOneArg(result, &PyId_difference_update, other);
4190+
_Py_IDENTIFIER(difference_update);
4191+
PyObject *tmp = _PyObject_CallMethodIdOneArg(
4192+
result, &PyId_difference_update, other);
41764193
if (tmp == NULL) {
41774194
Py_DECREF(result);
41784195
return NULL;
@@ -4273,34 +4290,29 @@ _PyDictView_Intersect(PyObject* self, PyObject *other)
42734290
static PyObject*
42744291
dictviews_or(PyObject* self, PyObject *other)
42754292
{
4276-
PyObject *result = PySet_New(self);
4277-
PyObject *tmp;
4278-
_Py_IDENTIFIER(update);
4279-
4280-
if (result == NULL)
4293+
PyObject *result = dictviews_to_set(self);
4294+
if (result == NULL) {
42814295
return NULL;
4296+
}
42824297

4283-
tmp = _PyObject_CallMethodIdOneArg(result, &PyId_update, other);
4284-
if (tmp == NULL) {
4298+
if (_PySet_Update(result, other) < 0) {
42854299
Py_DECREF(result);
42864300
return NULL;
42874301
}
4288-
4289-
Py_DECREF(tmp);
42904302
return result;
42914303
}
42924304

42934305
static PyObject*
42944306
dictviews_xor(PyObject* self, PyObject *other)
42954307
{
4296-
PyObject *result = PySet_New(self);
4297-
PyObject *tmp;
4298-
_Py_IDENTIFIER(symmetric_difference_update);
4299-
4300-
if (result == NULL)
4308+
PyObject *result = dictviews_to_set(self);
4309+
if (result == NULL) {
43014310
return NULL;
4311+
}
43024312

4303-
tmp = _PyObject_CallMethodIdOneArg(result, &PyId_symmetric_difference_update, other);
4313+
_Py_IDENTIFIER(symmetric_difference_update);
4314+
PyObject *tmp = _PyObject_CallMethodIdOneArg(
4315+
result, &PyId_symmetric_difference_update, other);
43044316
if (tmp == NULL) {
43054317
Py_DECREF(result);
43064318
return NULL;

0 commit comments

Comments
 (0)