18

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

想想 dictbb[a]

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

但正如关于特殊方法的文档所述

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

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

我在这里看到了很多问题,答案is帮助许多困惑的人,但我找不到这个问题的答案。id

4

2 回答 2

22

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

来自ceval.c

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

v这里w很简单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.)");
于 2013-03-14T00:24:20.827 回答
17

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

对象身份的运算符is和测试:当且仅当和是同一个对象时为真。is notx is yxy

作为“同一个对象”是不允许你覆盖的。如果你的对象与另一个对象不同,它就不能假装是。


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

设计FAQ和类似文件没有说。但我怀疑这主要是因为它使调试 Python 和一些更深层次的标准库模块变得更容易,因为知道有某种方法可以从解释器中验证两个名称是否确实引用了同一个对象,或者打印出来以id确保名称没有随着时间的推移而改变,等等。想象一下调试weakref,甚至pickle没有它。


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

所有主要的实现都通过在较低级别遵循身份概念来处理这个问题。CPython 比较PyObject*指针的值,Jython 标识比较 Java 引用,PyPyis对对象空间对象进行比较……</p>

值得一看PyPy 源代码,它要求“x is yxy是同一个对象”在两个方向上都为真。顶级表达式x is y为真当且仅当,无论对象wxwy在适当的对象空间中是什么,都wy.is_(wx)为真,并且is_实现为wy is wx. 因此,x is y在 N 级当且仅当y is x在 N-1 级。


请注意,这意味着您可以很容易地使用 PyPy 构建is 可以被覆盖的 Python 方言,只需附加到更高级别is_的 dunder 方法即可。__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之上实现吗?嗯,:idx is yid(x) == id(y)id

返回对象的“身份”。这是一个整数,保证该对象在其生命周期内是唯一且恒定的。具有不重叠生命周期的两个对象可能具有相同的id()值。

因此,id对象的 是唯一且在其生命周期内是恒定的,并且x is y当它们是同一个对象时为真,因此当当且当时x is y为真id(x) == id(y),对吗?

好吧,id可以反弹到任何你想要的,并且不允许影响is。如果您非常仔细地制定了定义(请记住,如果您放弃对 的builtins引用id,那么曾经存在的任何实现甚至都不能保证不再存在,或者如果确实存在则可以正常工作......),您可以is在顶部定义的默认实现id

但这将是一件奇怪的事情。在 CPython 中,id(x)只是“返回内存中对象的地址”,这与指向内存中对象的指针的值相同。但这只是 CPython 的产物;没有什么说其他实现必须将id用于身份比较的基础值作为整数返回。事实上,在用没有指针(可以转换为整数)的语言编写的实现中,您甚至不清楚如何做到这一点。在 PyPy 中,id对象的


至于__hash__,您误读了文档的重要部分。

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

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

于 2013-03-14T00:27:08.413 回答