【问题标题】:Purpose of overloading identity operator重载恒等运算符的目的
【发布时间】:2014-02-07 14:30:34
【问题描述】:

为什么在 Python 中不能使用 overload the identity comparison 运算符?其他所有比较运算符都可以自定义,那么为什么不进行身份比较呢?

【问题讨论】:

  • 因为应该是身份?重载== 是唯一有意义的。毕竟,必须至少有一位您可以信任的运营商。 =)
  • 出于实际目的,您希望有一种比较对象身份的有保证的方法,is 是 Python 的。对于某些事情(例如,布尔运算),拥有不可覆盖的运算符很重要。不过,您可以自定义== 的行为。您是否有一个特殊的用例,您觉得需要覆盖 is 但无法提供 __eq__ 方法来自定义 ==
  • 我没有特定的用例,只是提出了一个问题。

标签: python oop comparison-operators object-identity


【解决方案1】:

简单地说,因为身份算子的目的是为了检验身份。身份意味着“对象到底是什么”。运算符的全部意义在于能够测试两个对象是否“真的”是同一个对象,而不是根据它们自己的规则它们是否“想要”被认为是相同的。

【讨论】:

    【解决方案2】:

    你不能覆盖'is',因为它通过比较它们的内存地址(即指针比较)来测试两个对象是否是同一个对象。

    【讨论】:

    • @RyPeck,我同意 shv,因为它是指针比较,你不能覆盖它。
    • @Shv 我认为这不能解释为什么你不能覆盖它。我完全同意需要有一个不可覆盖的标识运算符,但仅仅因为is 是一个标识比较并不意味着它不能被覆盖。例如,Java 中默认的 equals 方法会进行身份比较,但它是可覆盖的。当然,Java 仍然提供==,它是不可 可覆盖的,并提供身份比较。我认为这里可能缺少的前提是“不可覆盖的身份比较是非常可取的,is 是 Python 的。”
    【解决方案3】:

    支持具有可变状态的对象的编程语言通常提供一个运算符,可以测试两个对象是否实际上是相同对象。在这种情况下,“相同”意味着对象实际上是相同的对象(例如,内存中的相同字节块(或者编译器设计者选择表示对象)。但是,对于许多类型的数据结构,有其他类型的等价关系对程序员来说可能更突出。例如,给定一个 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. ##
    

    在不具有可变状态的函数式编程语言中,甚至在具有可变状态但我们使用函数式样式的语言中,我们不会破坏性地修改这样的列表,而是 add 操作将返回一个新列表(可能与其他列表共享结构)。在这种情况下,任何元素序列都可能只有一个列表,因此我们可以:

    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 xy,因为即使xy 不是同一个对象,same as 也可能返回 true。

    在对象标识可能很重要的语言中,需要有一个不能被覆盖的对象标识运算符。引入一个可以以某种方式覆盖的相等运算符通常也很有用,这样可以很容易地检查两个对象是否以某种有用的方式等价(这将特定于对象的类型)。

    • 在Python中,恒等运算符为is,相等运算符为==,可以通过__eq__方法自定义。
    • 再举一个例子,在 Java 中,恒等运算符是 ==,相等运算符是 Object.equals(Object)

    值得注意的是,在许多语言中,相等运算符的默认实现对象标识。这很好,因为对象标识的测试通常比其他更复杂的相等关系要快得多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-24
      • 1970-01-01
      • 2013-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多