如果我在 Python 中调用 `None == x` ,“幕后”会发生什么?

Mar*_*ger 9 python equality comparison-operators python-3.x nonetype

我正在学习和使用 Python,我想出了以下测试代码(请注意,我不会编写这样的高效代码,但在学习新语言时,我喜欢尝试该语言的极端情况):

a = None    
print(None == a) # I expected True, I got True

b = 1
print(None == b) # I expected False, I got False

class MyNone:
    # Called if I compare some myMyNone == somethingElse
    def __eq__(self, __o: object) -> bool:
        return True

c = MyNone()
print (None == c) # !!! I expected False, I got True !!!
Run Code Online (Sandbox Code Playgroud)

请参阅代码示例的最后一行。

None == something明明不存在的东西怎么会None返回True呢?我本希望得到这样的结果something == None,但不是None == something

我预计它会None is something在幕后调用。

所以我认为问题归结为:单例对象的方法是什么样的以及我如何找到它?__eq__None


PS:我知道PEP-0008及其报价

与像 None 这样的单例的比较应该总是使用 is 或 is not 来完成,而不是使用相等运算符。

但我仍然想知道为什么print (None == c)在上面的例子中返回True.

kay*_*ya3 8

事实上,None的类型没有自己的__eq__方法;在Python中我们可以看到它显然继承自基类object

>>> type(None).__eq__
<slot wrapper '__eq__' of 'object' objects>
Run Code Online (Sandbox Code Playgroud)

但这并不是源代码中真正发生的事情。的实现可以在 CPython 源代码中None找到,我们可以看到:Objects/object.c

PyTypeObject _PyNone_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "NoneType",
    0,
    0,
    none_dealloc,       /*tp_dealloc*/ /*never called*/
    0,                  /*tp_vectorcall_offset*/
    0,                  /*tp_getattr*/
    0,                  /*tp_setattr*/
    // ...
    0,                  /*tp_richcompare */
    // ...
    0,                  /*tp_init */
    0,                  /*tp_alloc */
    none_new,           /*tp_new */
};
Run Code Online (Sandbox Code Playgroud)

我省略了大部分不相关的部分。这里重要的是_PyNone_Types tp_richcompareis 0,即空指针。这是在函数中检查的do_richcompare

    if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
        res = (*f)(v, w, op);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);
    }
    if (!checked_reverse_op && (f = Py_TYPE(w)->tp_richcompare) != NULL) {
        res = (*f)(w, v, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);
    }
Run Code Online (Sandbox Code Playgroud)

为不会说 C 的人翻译:

  • 如果左侧的tp_richcompare函数不为空,则调用它,如果其结果不为空,NotImplemented则返回该结果。
  • 否则,如果尚未检查反向*,并且右侧的tp_richcompare函数不为空,则调用它,如果结果不是,NotImplemented则返回该结果。

代码中还有一些其他分支,以防万一这些分支都没有返回结果。但这两个分支足以看出发生了什么。并不是type(None).__eq__return NotImplemented,而是该类型在 C 源代码中根本没有相应的函数。这意味着采用了第二个分支,因此是您观察到的结果。

checked_reverse_op*如果已经检查了反向方向,则设置该标志;如果右侧是左侧的严格子类型,则会发生这种情况,在这种情况下它优先。type(None)这不适用于这种情况,因为您的类之间没有子类型关系。


VPf*_*PfB 7

正如文档中所述:x==y调用x.__eq__(y),但None.__eq__(...)返回除自身NotImplemented之外的任何内容None(缺少部分:为什么?我不知道),因此 Python 尝试以相反的方式进行比较,并__eq__MyNone始终返回的中调用True

更新:(NoneNoneType)没有定义自己的并使用基于测试的__eq__默认值:如果参数相同或不同,则返回。(感谢共同作者)object.__eq__isTrueisNotImplemented

  • 缺少的部分是“type(None).__eq__”只是“object.__eq__”,只有当两个输入是同一对象(按标识,而不是按值)时,它才会返回“True”。但实际上,此代码中并未达到实现该行为的源代码部分,因为它的优先级低于右侧的 __eq__` 方法(当它有一个时)。详情请参阅我的回答。 (3认同)