Skip to content

Commit bc0f2ab

Browse files
committed
Expose dict_contains() and PyDict_Contains() with is about 10% faster
than PySequence_Contains() and more clearly applicable to dicts. Apply the new function in setobject.c where __contains__ checking is ubiquitous.
1 parent 3972457 commit bc0f2ab

5 files changed

Lines changed: 26 additions & 12 deletions

File tree

Doc/api/concrete.tex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,14 @@ \subsection{Dictionary Objects \label{dictObjects}}
18081808
Empties an existing dictionary of all key-value pairs.
18091809
\end{cfuncdesc}
18101810

1811+
\begin{cfuncdesc}{int}{PyDict_Contains}{PyObject *p, PyObject *key}
1812+
Determine if dictionary \var{p} contains \var{key}. If an item
1813+
in \var{p} is matches \var{key}, return \code{1}, otherwise return
1814+
\code{0}. On error, return \code{-1}. This is equivalent to the
1815+
Python expression \samp{\var{key} in \var{p}}.
1816+
\versionadded{2.4}
1817+
\end{cfuncdesc}
1818+
18111819
\begin{cfuncdesc}{PyObject*}{PyDict_Copy}{PyObject *p}
18121820
Returns a new dictionary that contains the same key-value pairs as
18131821
\var{p}.

Include/dictobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ PyAPI_FUNC(PyObject *) PyDict_Values(PyObject *mp);
100100
PyAPI_FUNC(PyObject *) PyDict_Items(PyObject *mp);
101101
PyAPI_FUNC(int) PyDict_Size(PyObject *mp);
102102
PyAPI_FUNC(PyObject *) PyDict_Copy(PyObject *mp);
103+
PyAPI_FUNC(int) PyDict_Contains(PyObject *mp, PyObject *key);
103104

104105
/* PyDict_Update(mp, other) is equivalent to PyDict_Merge(mp, other, 1). */
105106
PyAPI_FUNC(int) PyDict_Update(PyObject *mp, PyObject *other);

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ Build
231231
C API
232232
-----
233233

234+
- Added a new function, PyDict_Contains(d, k) which is like
235+
PySequence_Contains() but is specific to dictionaries and executes
236+
about 10% faster.
237+
234238
- Added three new macros: Py_RETURN_NONE, Py_RETURN_TRUE, and Py_RETURN_FALSE.
235239
Each return the singleton they mention after Py_INCREF()ing them.
236240

Objects/dictobject.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1814,10 +1814,11 @@ static PyMethodDef mapp_methods[] = {
18141814
{NULL, NULL} /* sentinel */
18151815
};
18161816

1817-
static int
1818-
dict_contains(dictobject *mp, PyObject *key)
1817+
int
1818+
PyDict_Contains(PyObject *op, PyObject *key)
18191819
{
18201820
long hash;
1821+
dictobject *mp = (dictobject *)op;
18211822

18221823
if (!PyString_CheckExact(key) ||
18231824
(hash = ((PyStringObject *) key)->ob_shash) == -1) {
@@ -1837,7 +1838,7 @@ static PySequenceMethods dict_as_sequence = {
18371838
0, /* sq_slice */
18381839
0, /* sq_ass_item */
18391840
0, /* sq_ass_slice */
1840-
(objobjproc)dict_contains, /* sq_contains */
1841+
(objobjproc)PyDict_Contains, /* sq_contains */
18411842
0, /* sq_inplace_concat */
18421843
0, /* sq_inplace_repeat */
18431844
};

Objects/setobject.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,13 @@ set_contains(PySetObject *so, PyObject *key)
143143
PyObject *tmp;
144144
int result;
145145

146-
result = PySequence_Contains(so->data, key);
146+
result = PyDict_Contains(so->data, key);
147147
if (result == -1 && PyAnySet_Check(key)) {
148148
PyErr_Clear();
149149
tmp = frozenset_dict_wrapper(((PySetObject *)(key))->data);
150150
if (tmp == NULL)
151151
return -1;
152-
result = PySequence_Contains(so->data, tmp);
152+
result = PyDict_Contains(so->data, tmp);
153153
Py_DECREF(tmp);
154154
}
155155
return result;
@@ -252,7 +252,7 @@ set_intersection(PySetObject *so, PyObject *other)
252252
}
253253

254254
while ((item = PyIter_Next(it)) != NULL) {
255-
if (PySequence_Contains(selfdata, item)) {
255+
if (PyDict_Contains(selfdata, item)) {
256256
if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
257257
Py_DECREF(it);
258258
Py_DECREF(result);
@@ -292,7 +292,7 @@ set_intersection_update(PySetObject *so, PyObject *other)
292292

293293
selfdata = so->data;
294294
while ((item = PyIter_Next(it)) != NULL) {
295-
if (PySequence_Contains(selfdata, item)) {
295+
if (PyDict_Contains(selfdata, item)) {
296296
if (PyDict_SetItem(newdict, item, Py_True) == -1) {
297297
Py_DECREF(newdict);
298298
Py_DECREF(it);
@@ -375,7 +375,7 @@ set_difference(PySetObject *so, PyObject *other)
375375
}
376376

377377
while ((item = PyIter_Next(it)) != NULL) {
378-
if (!PySequence_Contains(otherdata, item)) {
378+
if (!PyDict_Contains(otherdata, item)) {
379379
if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
380380
Py_XDECREF(otherset);
381381
Py_DECREF(it);
@@ -481,7 +481,7 @@ set_symmetric_difference_update(PySetObject *so, PyObject *other)
481481
return NULL;
482482

483483
while ((item = PyIter_Next(it)) != NULL) {
484-
if (PySequence_Contains(selfdata, item)) {
484+
if (PyDict_Contains(selfdata, item)) {
485485
if (PyDict_DelItem(selfdata, item) == -1) {
486486
Py_XDECREF(otherset);
487487
Py_DECREF(it);
@@ -541,7 +541,7 @@ set_symmetric_difference(PySetObject *so, PyObject *other)
541541
return NULL;
542542
}
543543
while ((item = PyIter_Next(it)) != NULL) {
544-
if (!PySequence_Contains(selfdata, item)) {
544+
if (!PyDict_Contains(selfdata, item)) {
545545
if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
546546
Py_DECREF(it);
547547
Py_DECREF(item);
@@ -562,7 +562,7 @@ set_symmetric_difference(PySetObject *so, PyObject *other)
562562
return NULL;
563563
}
564564
while ((item = PyIter_Next(it)) != NULL) {
565-
if (!PySequence_Contains(otherdata, item)) {
565+
if (!PyDict_Contains(otherdata, item)) {
566566
if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
567567
Py_DECREF(it);
568568
Py_DECREF(item);
@@ -634,7 +634,7 @@ set_issubset(PySetObject *so, PyObject *other)
634634

635635
otherdata = ((PySetObject *)other)->data;
636636
while ((item = PyIter_Next(it)) != NULL) {
637-
if (!PySequence_Contains(otherdata, item)) {
637+
if (!PyDict_Contains(otherdata, item)) {
638638
Py_DECREF(it);
639639
Py_DECREF(item);
640640
Py_RETURN_FALSE;

0 commit comments

Comments
 (0)