diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -1767,6 +1767,10 @@ PyObject *newval = NULL; PyObject *key = NULL; PyObject *one = NULL; + PyObject *mapping_getitem = NULL; + PyObject *mapping_setitem = NULL; + PyObject *dict_getitem = NULL; + PyObject *dict_setitem = NULL; if (!PyArg_UnpackTuple(args, "_count_elements", 2, 2, &mapping, &iterable)) return NULL; @@ -1781,7 +1785,32 @@ return NULL; } - if (PyDict_CheckExact(mapping)) { + /* The fast path can be used whenever + * Counter.__getitem__ is dict.__getitem__ and + * when Counter.__setitem__ is dict.__setitem__. + */ + mapping_getitem = PyObject_GetAttrString((PyObject *)Py_TYPE(mapping), "__getitem__"); + if (mapping_getitem == NULL) { + PyErr_Clear(); + goto done; + } + dict_getitem = PyObject_GetAttrString((PyObject *)&PyDict_Type, "__getitem__"); + if (dict_getitem == NULL) { + PyErr_Clear(); + goto done; + } + mapping_setitem = PyObject_GetAttrString((PyObject *)Py_TYPE(mapping), "__setitem__"); + if (mapping_setitem == NULL) { + PyErr_Clear(); + goto done; + } + dict_setitem = PyObject_GetAttrString((PyObject *)&PyDict_Type, "__setitem__"); + if (dict_setitem == NULL) { + PyErr_Clear(); + goto done; + } + + if (mapping_getitem == dict_getitem && mapping_setitem == dict_setitem) { while (1) { key = PyIter_Next(it); if (key == NULL) @@ -1825,10 +1854,15 @@ } } +done: Py_DECREF(it); Py_XDECREF(key); Py_XDECREF(newval); - Py_DECREF(one); + Py_XDECREF(one); + Py_XDECREF(mapping_getitem); + Py_XDECREF(mapping_setitem); + Py_XDECREF(dict_getitem); + Py_XDECREF(dict_setitem); if (PyErr_Occurred()) return NULL; Py_RETURN_NONE;