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.
diff --git a/Doc/api/concrete.tex b/Doc/api/concrete.tex
index bf8d438..67852db 100644
--- a/Doc/api/concrete.tex
+++ b/Doc/api/concrete.tex
@@ -1808,6 +1808,14 @@
   Empties an existing dictionary of all key-value pairs.
 \end{cfuncdesc}
 
+\begin{cfuncdesc}{int}{PyDict_Contains}{PyObject *p, PyObject *key}
+  Determine if dictionary \var{p} contains \var{key}.  If an item
+  in \var{p} is matches \var{key}, return \code{1}, otherwise return
+  \code{0}.  On error, return \code{-1}.  This is equivalent to the
+  Python expression \samp{\var{key} in \var{p}}.
+  \versionadded{2.4}			 
+\end{cfuncdesc}
+
 \begin{cfuncdesc}{PyObject*}{PyDict_Copy}{PyObject *p}
   Returns a new dictionary that contains the same key-value pairs as
   \var{p}.
diff --git a/Include/dictobject.h b/Include/dictobject.h
index c8ae912..554b82e 100644
--- a/Include/dictobject.h
+++ b/Include/dictobject.h
@@ -100,6 +100,7 @@
 PyAPI_FUNC(PyObject *) PyDict_Items(PyObject *mp);
 PyAPI_FUNC(int) PyDict_Size(PyObject *mp);
 PyAPI_FUNC(PyObject *) PyDict_Copy(PyObject *mp);
+PyAPI_FUNC(int) PyDict_Contains(PyObject *mp, PyObject *key);
 
 /* PyDict_Update(mp, other) is equivalent to PyDict_Merge(mp, other, 1). */
 PyAPI_FUNC(int) PyDict_Update(PyObject *mp, PyObject *other);
diff --git a/Misc/NEWS b/Misc/NEWS
index f7b1e96..a3f3a18 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -231,6 +231,10 @@
 C API
 -----
 
+- Added a new function, PyDict_Contains(d, k) which is like
+  PySequence_Contains() but is specific to dictionaries and executes
+  about 10% faster.
+
 - Added three new macros: Py_RETURN_NONE, Py_RETURN_TRUE, and Py_RETURN_FALSE.
   Each return the singleton they mention after Py_INCREF()ing them.
 
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 5d22404..0cf71b5 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -1814,10 +1814,11 @@
 	{NULL,		NULL}	/* sentinel */
 };
 
-static int
-dict_contains(dictobject *mp, PyObject *key)
+int
+PyDict_Contains(PyObject *op, PyObject *key)
 {
 	long hash;
+	dictobject *mp = (dictobject *)op;
 
 	if (!PyString_CheckExact(key) ||
 	    (hash = ((PyStringObject *) key)->ob_shash) == -1) {
@@ -1837,7 +1838,7 @@
 	0,					/* sq_slice */
 	0,					/* sq_ass_item */
 	0,					/* sq_ass_slice */
-	(objobjproc)dict_contains,		/* sq_contains */
+	(objobjproc)PyDict_Contains,		/* sq_contains */
 	0,					/* sq_inplace_concat */
 	0,					/* sq_inplace_repeat */
 };
diff --git a/Objects/setobject.c b/Objects/setobject.c
index ce3f84e..c060077 100644
--- a/Objects/setobject.c
+++ b/Objects/setobject.c
@@ -143,13 +143,13 @@
 	PyObject *tmp;
 	int result;
 
-	result = PySequence_Contains(so->data, key);
+	result = PyDict_Contains(so->data, key);
 	if (result == -1 && PyAnySet_Check(key)) {
 		PyErr_Clear();
 		tmp = frozenset_dict_wrapper(((PySetObject *)(key))->data);
 		if (tmp == NULL)
 			return -1;
-		result = PySequence_Contains(so->data, tmp);
+		result = PyDict_Contains(so->data, tmp);
 		Py_DECREF(tmp);
 	}
 	return result;
@@ -252,7 +252,7 @@
 	}
 
 	while ((item = PyIter_Next(it)) != NULL) {
-		if (PySequence_Contains(selfdata, item)) {
+		if (PyDict_Contains(selfdata, item)) {
 			if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
 				Py_DECREF(it);
 				Py_DECREF(result);
@@ -292,7 +292,7 @@
 
 	selfdata = so->data;
 	while ((item = PyIter_Next(it)) != NULL) {
-		if (PySequence_Contains(selfdata, item)) {
+		if (PyDict_Contains(selfdata, item)) {
 			if (PyDict_SetItem(newdict, item, Py_True) == -1) {
 				Py_DECREF(newdict);
 				Py_DECREF(it);
@@ -375,7 +375,7 @@
 	}
 
 	while ((item = PyIter_Next(it)) != NULL) {
-		if (!PySequence_Contains(otherdata, item)) {
+		if (!PyDict_Contains(otherdata, item)) {
 			if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
 				Py_XDECREF(otherset);
 				Py_DECREF(it);
@@ -481,7 +481,7 @@
 		return NULL;
 
 	while ((item = PyIter_Next(it)) != NULL) {
-		if (PySequence_Contains(selfdata, item)) {
+		if (PyDict_Contains(selfdata, item)) {
 			if (PyDict_DelItem(selfdata, item) == -1) {
 				Py_XDECREF(otherset);
 				Py_DECREF(it);
@@ -541,7 +541,7 @@
 		return NULL;
 	}
 	while ((item = PyIter_Next(it)) != NULL) {
-		if (!PySequence_Contains(selfdata, item)) {
+		if (!PyDict_Contains(selfdata, item)) {
 			if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
 				Py_DECREF(it);
 				Py_DECREF(item);
@@ -562,7 +562,7 @@
 		return NULL;
 	}
 	while ((item = PyIter_Next(it)) != NULL) {
-		if (!PySequence_Contains(otherdata, item)) {
+		if (!PyDict_Contains(otherdata, item)) {
 			if (PyDict_SetItem(tgtdata, item, Py_True) == -1) {
 				Py_DECREF(it);
 				Py_DECREF(item);
@@ -634,7 +634,7 @@
 
 	otherdata = ((PySetObject *)other)->data;
 	while ((item = PyIter_Next(it)) != NULL) {
-		if (!PySequence_Contains(otherdata, item)) {
+		if (!PyDict_Contains(otherdata, item)) {
 			Py_DECREF(it);
 			Py_DECREF(item);
 			Py_RETURN_FALSE;