Before we special-case strings more, we should fix what we've got: Martin (IIRC) inserted a fast path at the start of do_richcmp early in the 2.2 cycle: if (v->ob_type == w->ob_type && (f = v->ob_type->tp_compare) != NULL && !PyInstance_Check(v)) { int c; richcmpfunc f1; if ((f1 = RICHCOMPARE(v->ob_type)) != NULL) { /* If the type has richcmp, try it first. try_rich_compare would try it two-sided, which is not needed since we've a single type only. */ res = (*f1)(v, w, op); if (res != Py_NotImplemented) return res; Py_DECREF(res); } c = (*f)(v, w); if (c < 0 && PyErr_Occurred()) return NULL; return convert_3way_to_object(op, c); } Unfortunately, strings lost their tp_compare slot (due to some other "optimization"?), so despite that this is *trying* to special-case rich compares of same-type builtin objects, for strings the v->ob_type->tp_compare != NULL guard at the start fails today, so this fast path isn't taken for string compares of any kind anymore. If it were taken, string_richcompare would get out quickly (without a strcmp) for EQ compare of identical string objects. Note that saying x == x is true regardless of the type of x would prevent defining a correct IEEE-754 equality operator (IOW, if we're saying __eq__ is user-defined, we have to let users define it any way they want -- and at least one major std requires an insane <0.3 wink> definition of equality). BTW, PyInstance_Check() there looks suspect now too, since instances of new-style classes don't pass PyInstance_Check() (so *can* get into this fast path).