在Python中,运行如下的代码
nan = float("nan") print nan == nan print [nan] == [nan]
其输出结果分别为False 和 True,于是决定去查看相关的代码查找原因。
首先,float中nan在ieee标准中有约定,与任何值都不相等,所以主要需要确认的就是列表的比较规则。
static PyObject * list_richcompare(PyObject *v, PyObject *w, int op)
list的比较使用的是list_richcompare函数,接收3个参数,待比较的两个引用以及操作符
if (!PyList_Check(v) || !PyList_Check(w)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } vl = (PyListObject *)v; wl = (PyListObject *)w;
首先检查输入的待比较的元素是否是列表,然后转换指针的类型为列表
if (Py_SIZE(vl) != Py_SIZE(wl) && (op == Py_EQ || op == Py_NE)) { /* Shortcut: if the lengths differ, the lists differ */ PyObject *res; if (op == Py_EQ) res = Py_False; else res = Py_True; Py_INCREF(res); return res; }
对于,==和!=两个操作符,如果两个列表的大小不想等,则可以直接返回False或者True的结果,无须再进行其他比较。
for (i = 0; i < Py_SIZE(vl) && i < Py_SIZE(wl); i++) { int k = PyObject_RichCompareBool(vl->ob_item[i], wl->ob_item[i], Py_EQ); if (k < 0) return NULL; if (!k) break; }
从头开始比较列表的中元素,直到有一个列表遍历完或者有不相等的情况则中止,搜索出第一个不相等的成员的索引值。
其中对于每个元素的比较,调用的是PyObject_RichCompareBool方法
if (i >= Py_SIZE(vl) || i >= Py_SIZE(wl)) { /* No more items to compare -- compare sizes */ Py_ssize_t vs = Py_SIZE(vl); Py_ssize_t ws = Py_SIZE(wl); int cmp; PyObject *res; switch (op) { case Py_LT: cmp = vs < ws; break; case Py_LE: cmp = vs <= ws; break; case Py_EQ: cmp = vs == ws; break; case Py_NE: cmp = vs != ws; break; case Py_GT: cmp = vs > ws; break; case Py_GE: cmp = vs >= ws; break; default: return NULL; /* cannot happen */ } if (cmp) res = Py_True; else res = Py_False; Py_INCREF(res); return res; }
如果至少有一个列表遍历结束,则所有比较过的元素都一致。
则哪个列表的尺寸长,那么该列表就大。如果两个列表的尺寸相同,则这两个列表就相等。只需针对不同的op,返回不同的结果即可。
if (op == Py_EQ) { Py_INCREF(Py_False); return Py_False; } if (op == Py_NE) { Py_INCREF(Py_True); return Py_True; }
如果在两列表的尺寸范围内,找到了不相等的成员,就说明这两个列表不相等,为==和!=提供捷径。
return PyObject_RichCompare(vl->ob_item[i], wl->ob_item[i], op);
当op不为Py_EQ或Py_NE时,就将第一个不相等的成员的比较结果作为,这两个列表的比较结果。
对于PyObject_RichCompareBool
/* Return -1 if error; 1 if v op w; 0 if not (v op w). */ int PyObject_RichCompareBool(PyObject *v, PyObject *w, int op) { PyObject *res; int ok; /* Quick result when objects are the same. Guarantees that identity implies equality. */ if (v == w) { if (op == Py_EQ) return 1; else if (op == Py_NE) return 0; } res = PyObject_RichCompare(v, w, op); if (res == NULL) return -1; if (PyBool_Check(res)) ok = (res == Py_True); else ok = PyObject_IsTrue(res); Py_DECREF(res); return ok; }
在比较之前,会首先看两个比较元素的引用地址,如果相同则视为相等。
所以nan在列表中比较时由于引用地址一样,也会被视为相等。