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

xzabzqsa  于 2022-12-10  发布在  Python
关注(0)|答案(2)|浏览(128)

我正在学习和使用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 !!!

"请看最后一行"
为什么None == something,其中的东西显然不是None,返回True?我希望something == None的结果是这样的,但None == something的结果不是这样。
我预计它会在幕后调用None is something
所以我认为问题归结为:None单例对象的__eq__方法是什么样子的?我是如何发现的?
PS:我知道PEP-0008和它的报价
与单例(如None)的比较应始终使用is或is not,而绝不要使用相等运算符。
但是我 * 仍然 * 想知道为什么上面例子中的print (None == c)返回True

f5emj3cl

f5emj3cl1#

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

>>> type(None).__eq__
<slot wrapper '__eq__' of 'object' objects>

但是这并不是源代码中真正发生的事情,None的实现可以在CPython源代码的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 */
};

我省略了大部分不相关的部分。这里重要的是_PyNone_Typetp_richcompare0,即一个空指针。这在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);
    }

为那些不会说C的人翻译:

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

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

  • 如果已经检查了反向,则设置标志checked_reverse_op;如果右边的是左边的严格子类型,就会发生这种情况,在这种情况下,它具有优先级。但在这种情况下,这并不适用,因为type(None)和你的类之间没有子类型关系。
9jyewag0

9jyewag02#

如文件所述:x==y调用x.__eq__(y),但None.__eq__(...)为除None本身之外的任何内容返回NotImplemented(缺少部分:为什么?我不知道),所以Python尝试了相反的比较,从MyNone调用__eq__MyNone总是返回True
更新:NoneNoneType类)没有定义自己的__eq__,而是使用基于is测试的默认object.__eq__:如果参数is相同,则返回True,否则返回NotImplemented

相关问题