为什么在 Python 中不能重载恒等比较运算符?每个其他比较运算符都可以自定义,那么为什么不进行身份比较呢?
3 回答
你不能覆盖'is',因为它通过比较它们的内存地址(即指针比较)来测试两个对象是否是同一个对象。
简单来说,因为身份算子的目的是为了检验身份。身份意味着“对象真正是什么”。运算符的重点是能够测试两个对象是否“真的”是同一个对象,而不是根据自己的规则它们是否“想要”被认为是相同的。
支持具有可变状态的对象的编程语言通常提供一个运算符,可以测试两个对象是否实际上是同一个对象。在这种情况下,“相同”意味着对象实际上是相同的对象(例如,内存中的相同字节块(或者编译器设计者选择表示对象)。但是,对于许多类型的数据结构,有其他类型的等价关系可能对程序员来说更突出。例如,给定一个 List 接口,程序员可能只关心两个列表是否包含相同顺序的等价元素。这只有在有一些的情况下才真正重要区分具有等价元素的两个 List 的方法。因为许多编程语言都支持可变状态,改变对象状态的操作正是可以区分此类对象的一种方式。
例如,给定列表的可变实现,我们可能有:
x = make a list of 1 2 3
y = x
z = make a list of 1 2 3 4
x same as y? yes.
x equal to y? yes.
x same as z? no.
x equal to z? no.
add 4 to end of x
x same as y? yes.
x equal to y? yes.
x same as z? no.
x equal to z? yes. ##
在没有可变状态的函数式编程语言中,甚至在具有可变状态但我们使用函数式样式的语言中,我们不会像这样破坏性地修改列表,而是添加操作会返回一个新列表(可能与其他列表共享结构)。在这种情况下,任何元素序列都可能只有一个列表,因此我们可以:
x = make a list of 1 2 3
y = x
z = make a list of 1 2 3 4
x same as y? yes.
x equal to y? yes.
x same as z? no.
x equal to z? no.
x' = add 4 to end of x
x same as y? yes.
x equal to y? yes.
x same as z? no.
x equal to z? no.
x same as x'? no.
x equal to x'? no.
x' same as x? no.
x' equal to x? no.
x' same as z? yes. ## or no, depending on implementation
x' equal to z? yes.
x' same as x'? yes.
x' equal to x'? yes.
事实是
x same as y? yes.
x equal to y? yes.
x same as z? no.
x equal to z? no.
始终保持不变对于推理程序的行为很有用。
当我们以面向对象的方式进行编程时,对象标识是一个重要的概念,并且实际上是语言的原语之一,就像布尔运算符或数值比较一样。如果它可以被覆盖,则无法执行一整类优化,并且您可能会引入一些很难追踪的错误。例如,考虑(也许是人为的例子):
# frob x and y, but never frob an object twice
frobBoth x y
if x same as y # **
frob x
else
frob x
frob y
end if
如果您可以覆盖same as
,那么您可能不会frob
同时使用x
and y
,因为same as
即使x
andy
不是同一个对象也可能返回 true 。
在对象标识可能很重要的语言中,需要有一个不能被覆盖的对象标识运算符。引入一个可以以某种方式覆盖的相等运算符通常也很有用,这样可以很容易地检查两个对象是否以某种有用的方式等价(这将特定于对象的类型)。
- 在 Python 中,恒等运算符 is
is
和等式运算符是==
,可以通过__eq__
方法自定义。 - 再举一个例子,在 Java 中,恒等运算符是
==
,而相等运算符是Object.equals(Object)
。
值得注意的是,在许多语言中,相等运算符的默认实现是对象标识。这可能很好,因为对象标识通常比其他更复杂的相等关系更快地测试。