`is`运算符在Python中使用__magic__方法吗?

Chr*_*ing 13 python operators

IS运算符用于测试的身份.

我想知道is运算符和id()函数是否调用任何__magic__方法,==调用方式__eq__.

我有一些乐趣退房__hash__:

class Foo(object):
    def __hash__(self):
        return random.randint(0, 2 ** 32)

a = Foo()
b = {}
for i in range(5000):
    b[a] = i
Run Code Online (Sandbox Code Playgroud)

想想字典b和价值b[a]

每个后续查找d[a]都是一个KeyError或一个随机整数.

但正如关于特殊方法文档说明的那样

[ 默认实现 ] x.__hash__()返回id(x).

因此,有只是周围的其他方式两者之间的关系,但.

我见过很多问题isid在此处,将答案已经帮助许多糊涂思想,但我无法找到答案,这一个.

nne*_*neo 17

不,is是一个直指针比较,id只返回对象的地址转换为long.

来自ceval.c:

case PyCmp_IS:
    res = (v == w);
    break;
case PyCmp_IS_NOT:
    res = (v != w);
    break;
Run Code Online (Sandbox Code Playgroud)

vw这里仅仅是PyObject *.

来自bltinmodule.c:

static PyObject *
builtin_id(PyObject *self, PyObject *v)
{
    return PyLong_FromVoidPtr(v);
}

PyDoc_STRVAR(id_doc,
"id(object) -> integer\n\
\n\
Return the identity of an object. This is guaranteed to be unique among\n\
simultaneously existing objects. (Hint: it's the object's memory address.)");
Run Code Online (Sandbox Code Playgroud)

  • +1挖掘源(我懒得做;)) (2认同)
  • 一行代码值得一千张图片. (2认同)
  • 这个答案是特定于CPython的。在其他实现中(至少是Jython和PyPy),“ is”不是指针比较,并且“ id”不返回地址。 (2认同)

aba*_*ert 12

简短的回答是:不,他们没有.正如您链接的文档所说:

对象标识的运算符isis not测试:x is y当且仅当xy是同一个对象时才是真的.

作为"同一个对象"并不是你可以覆盖的东西.如果您的对象与另一个对象不是同一个对象,则无法假装.


所以为什么?让你覆盖is和/或id?有什么害处?显然,它几乎总是一件愚蠢的事情,但如果你努力尝试,Python会让你做很多愚蠢的事情.

设计FAQ和类似文件没有说.但我怀疑它主要是因为它更容易调试Python和一些更深层次的标准库模块,知道有一些方法,从解释器中,验证两个名称确实引用同一个对象,或打印出来将id确保一个名字并没有随着时间的推移发生变化,等等想象调试weakref,甚至pickle,没有这一点.


那么,"同一个对象"究竟是什么意思呢?嗯,这取决于口译员.显然,不可能在语言级别区分同一对象的两个实例,也可能在解释器级别区分(特别是因为有一个定义良好的API用于插入大多数解释器实现).

所有主要实现都通过推迟较低级别的身份概念来处理此问题.CPython比较PyObject*指针的值,Jython身份 - 比较Java引用,PyPy is在对象空间对象上做...

值得一看的是PyPy源,它要求" x is yiff x并且y是同一个对象"才能在两个方向都成立.顶级表达式x is y为true,iff,无论对象wxwy适当的对象空间是什么,wy.is_(wx)都是真的,并is_实现为wy is wx.所以,x is yy is xN-1级的N级.


请注意,这意味着您可以非常轻松地使用PyPy来构建Python的方言,只需附加到更高级别的dunder方法is 即可覆盖.但是有一种更简单的方法可以做同样的事情:is___is__

def is_(x, y):
    if hasattr(x, '__is__'):
        return x.__is__(y)
    elif hasattr(y, '__is__'):
        return y.__is__(x)
    else:
        return x is y
Run Code Online (Sandbox Code Playgroud)

现在玩游戏is_(x, y)而不是x is y,看看你是否可以在修改解释器之前找到任何有趣的麻烦(即使它不是那么难,在这种情况下).


那么,is有什么用id呢?可以isid-eg 之上实现,x is y只是检查id(x) == id(y)?好吧,id:

返回对象的"标识".这是一个整数,在该生命周期内保证该对象是唯一且恒定的.具有非重叠寿命的两个对象可以具有相同的id()值.

因此,id一个对象在其生命周期中是唯一且恒定的,x is y如果它们是同一个对象x is y则是真的,因此iff是对的id(x) == id(y),对吧?

好吧,id可以反弹到任何你想要的,这是不允许影响is.如果你非常谨慎地制定了这个定义(请记住,如果你丢弃了对它的builtins引用id,那么过去的任何实现都不再保证存在,或者如果它确实存在就能正常工作......),你可以is在顶层定义的默认实现id.

但这样做会很奇怪.在CPython中,id(x)只是"返回内存中对象的地址",这与指向内存中对象的指针的值相同.但这只是CPython的一件神器; 没有什么可以说其他实现必须将id用于身份比较的基础值作为整数返回.事实上,在一个没有指针的语言(可以转换为整数)中编写的实现中,你甚至不知道如何做到这一点.在PyPy中,id对象的甚至可以是在第一次访问和存储在对象空间中的字典中时计算的值,由对象本身键入.


至于__hash__,你误读了文档的一个重要部分.

[...] x.__hash__()返回id(x).

您通过的部分清楚地表明,这仅适用于用户定义类(不重新定义__hash__)的实例.例如,显然不是这样tuple.简而言之,身份与散列无关,除了对于某些对象,身份是一个方便的散列值.