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 b
和b[a]
d[a]
的每次后续查找都是KeyError
或随机整数。
但是作为docs on the special methods州
[ 的默认实现] x。
__hash__
()返回id(x)。
所以两者之间的是关系,但恰恰相反。
答案 0 :(得分:16)
不,is
是直接指针比较,id
只返回投射到long
的对象的地址。
来自ceval.c
:
case PyCmp_IS:
res = (v == w);
break;
case PyCmp_IS_NOT:
res = (v != w);
break;
v
和w
这里只是PyObject *
。
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)
简短的回答是:不,他们没有。正如您链接的文档所说:
运算符
is
和is not
测试对象标识:当且仅当x is y
和x
是同一个对象时,y
才为真。
成为“同一个对象”不是你可以覆盖的东西。如果你的对象与另一个对象不是同一个对象,它就不能假装。
那么,为什么?让您覆盖is
和/或id
会有什么危害?显然,它几乎总是一件蠢事,但如果你努力的话,Python会让你做很多蠢事。
设计常见问题解答和类似文件没有说明。但我怀疑它主要是因为它更容易调试Python和一些更深层次的标准库模块,知道有一些方法,从解释器中,验证两个名称确实引用同一个对象,或打印出来id
以确保名称没有随时间变化等。想象一下调试weakref
,甚至pickle
,如果没有。
那么,“同一个对象”究竟是什么意思呢?嗯,这取决于口译员。显然,不可能在语言级别区分同一对象的两个实例,也可能在解释器级别区分(特别是因为有一个定义良好的API用于插入大多数解释器实现)。
所有主要实现都通过在较低级别推迟身份概念来处理此问题。 CPython比较PyObject*
指针的值,Jython身份 - 比较Java引用,PyPy在对象空间对象上做is
......
值得查看PyPy source,这要求“x is y
iff x
和y
是同一个对象”才能在两个方向都成立。顶级表达式x is y
为true,如果相应的对象空间中的对象wx
和wy
为wy.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}}引用,那么以前的任何实现都不会保证不再存在,或者如果它已经正常工作确实存在...),您可以1>}在id(x) == id(y)
的默认实现之上定义id
。
但这样做会很奇怪。在CPython中,is
只是“返回内存中对象的地址”,这与指向内存中对象的指针的值相同。但这只是CPython的一件神器;没有什么可以说其他实现必须使builtins
将用于身份比较的基础值作为整数返回。事实上,在一个没有指针的语言(可以转换为整数)中编写的实现中,你甚至不知道如何做到这一点。在PyPy中,对象的id
甚至可以是第一次访问和存储在对象空间中的字典中时计算的值,由对象本身键入。
至于is
,你误读了文档的一个重要部分。
[...]
id
返回id(x)
。
您通过的部分清楚地表明,这仅适用于用户定义的类的实例(不重新定义id
)。例如,id
显然不是这样。简而言之,身份与散列无关,除了对于某些对象,身份是一个方便的散列值。