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

时间:2013-03-14 00:21:55

标签: 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

考虑dict bb[a]

的值
  

d[a]的每次后续查找都是KeyError或随机整数。

但是作为docs on the special methods

  

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

所以两者之间的关系,但恰恰相反。

我在isid看到了很多questions,而answers已经帮助了很多confused人,但我找不到这个答案。

2 个答案:

答案 0 :(得分:16)

不,is是直接指针比较,id只返回投射到long的对象的地址。

来自ceval.c

case PyCmp_IS:
    res = (v == w);
    break;
case PyCmp_IS_NOT:
    res = (v != w);
    break;

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.)");

答案 1 :(得分:12)

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

  

运算符isis not测试对象标识:当且仅当x is yx是同一个对象时,y才为真。

成为“同一个对象”不是你可以覆盖的东西。如果你的对象与另一个对象不是同一个对象,它就不能假装。


那么,为什么?让您覆盖is和/或id会有什么危害?显然,它几乎总是一件蠢事,但如果你努力的话,Python会让你做很多蠢事。

设计常见问题解答和类似文件没有说明。但我怀疑它主要是因为它更容易调试Python和一些更深层次的标准库模块,知道有一些方法,从解释器中,验证两个名称确实引用同一个对象,或打印出来id以确保名称没有随时间变化等。想象一下调试weakref,甚至pickle,如果没有。


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

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

值得查看PyPy source,这要求“x is y iff xy是同一个对象”才能在两个方向都成立。顶级表达式x is y为true,如果相应的对象空间中的对象wxwywy.is_(wx)为真,并且is_实现为wy is wx。因此,{N}级为{ifer x is y的{​​{1}}。


请注意,这意味着只需将y is x附加到dunder方法{{} {} 1}}在更高的层次。但是有一种更简单的方法可以做同样的事情:

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 is_(x, y)有什么关系呢?可以在x is y之上实施is - 例如,id只检查is?好吧,id

  

返回对象的“标识”。这是一个整数,在该生命周期内保证该对象是唯一且恒定的。具有非重叠生存期的两个对象可能具有相同的id值。

因此,对象的x is y在其生命周期内是唯一且不变的,id(x) == id(y)如果它们是同一个对象则为真,因此id()如果{{1}则为真,对吧?

好吧,id可以反弹到你想要的任何地方,并且不允许影响x is y。如果你非常仔细地制定了这个定义(请记住,如果你丢弃对x is y的{​​{1}}引用,那么以前的任何实现都不会保证不再存在,或者如果它已经正常工作确实存在...),您可以}在id(x) == id(y)的默认实现之上定义id

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


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

  

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

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