【问题标题】:Python: Why does the int class not have rich comparison operators like `__lt__()`?Python:为什么 int 类没有像 `__lt__()` 这样丰富的比较运算符?
【发布时间】:2012-06-04 07:31:01
【问题描述】:

很好奇。

我注意到(至少在 py 2.6 和 2.7 中)float 具有所有熟悉的丰富比较功能:__lt__()__gt____eq__ 等。

>>> (5.0).__gt__(4.5)
True

int 没有

>>> (5).__gt__(4)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'int' object has no attribute '__gt__'

这对我来说很奇怪,因为操作员本身工作正常

>>> 5 > 4
True

字符串也支持比较函数

>>> "hat".__gt__("ace")
True

但是int 的全部是__cmp__()

对我来说似乎很奇怪,所以我想知道为什么会这样。

刚刚经过测试,它在 python 3 中按预期工作,所以我假设一些遗留原因。不过还是想听听正确的解释;)

【问题讨论】:

    标签: python language-design language-history


    【解决方案1】:

    __cmp__() 是老式的比较方式,是仅在 Python 2.1 中引入的deprecated in favor of the rich operators__lt____le__ 等)。从 2.7.x 开始,转换可能尚未完成——而在 Python 3.x 中,__cmp__ 已完全删除。

    Haskell 有我见过的最优雅的实现——作为Ord(序数)数据类型,您只需要定义&lt;= 的工作方式,并且类型类本身提供默认实现&lt;=&gt;&gt;= 就这两个而言(如果您愿意,欢迎您定义自己)。你可以自己用 Python 编写这样的类,不知道为什么这不是默认的;可能是性能原因。

    【讨论】:

      【解决方案2】:

      如果我们看一下PEP 207 for Rich Comparisions,最后会看到这个有趣的句子:

      已经存在的处理整数比较的内联仍然适用,不会导致最常见情况下的性能成本。

      所以似乎在 2.x 中对整数比较进行了优化。如果我们take a look at the source code 可以找到这个:

      case COMPARE_OP:
          w = POP();
          v = TOP();
          if (PyInt_CheckExact(w) && PyInt_CheckExact(v)) {
              /* INLINE: cmp(int, int) */
              register long a, b;
              register int res;
              a = PyInt_AS_LONG(v);
              b = PyInt_AS_LONG(w);
              switch (oparg) {
              case PyCmp_LT: res = a <  b; break;
              case PyCmp_LE: res = a <= b; break;
              case PyCmp_EQ: res = a == b; break;
              case PyCmp_NE: res = a != b; break;
              case PyCmp_GT: res = a >  b; break;
              case PyCmp_GE: res = a >= b; break;
              case PyCmp_IS: res = v == w; break;
              case PyCmp_IS_NOT: res = v != w; break;
              default: goto slow_compare;
              }
              x = res ? Py_True : Py_False;
              Py_INCREF(x);
          }
          else {
            slow_compare:
              x = cmp_outcome(oparg, v, w);
          }
      

      因此,在 2.x 中似乎有一个现有的性能优化 - 通过允许 C 代码直接比较整数 - 如果实现了丰富的比较运算符,则不会保留。

      现在在 Python 3 中,__cmp__ 不再受支持,因此必须有丰富的比较运算符。现在,据我所知,这不会导致性能下降。例如,比较:

      Python 2.7.1 (r271:86832, Jun 16 2011, 16:59:05) 
      [GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
      Type "help", "copyright", "credits" or "license" for more information.
      >>> import timeit
      >>> timeit.timeit("2 < 1")
      0.06980299949645996
      

      到:

      Python 3.2.3 (v3.2.3:3d0686d90f55, Apr 10 2012, 11:25:50) 
      [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
      Type "help", "copyright", "credits" or "license" for more information.
      >>> import timeit
      >>> timeit.timeit("2 < 1")
      0.06682920455932617
      

      因此,似乎存在类似的优化,但我的猜测是,当考虑向后兼容性时,将它们全部放在 2.x 分支中的变化太大了。

      在 2.x 中,如果您想要丰富的比较方法,您可以通过 operator module 获得它们:

      >>> import operator
      >>> operator.gt(2,1)
      True
      

      【讨论】:

        【解决方案3】:

        正如 hircus 所说,__cmp__ 样式比较在 Python 3 中是 deprecated in favor of the rich operators__lt__,...)。最初,比较是使用 __cmp__ 实现的,但在某些类型/情况下,简单的 @987654325 @ 运算符是不够的(例如,Color 类的实例可以支持==!=,但不支持&lt;&gt;),所以添加了丰富的比较运算符,留下__cmp__ 以供向后使用兼容性。遵循 Python 的“应该有一种——最好只有一种——明显的方式来做到这一点”的哲学理念,1在 Python 3 中删除了遗留支持,因为可能会牺牲向后兼容性。

        在 Python 2 中,虽然 int 仍然使用 __cmp__ 以免破坏向后兼容性,但并非所有浮点数都小于、大于或等于其他浮点数(例如,(float('nan') &lt; 0.0, float('nan') == 0.0, float('nan') &gt; 0.0) 的计算结果为(False, False, False),那么float('nan').__cmp__(0.0)应该返回什么?),所以float需要使用较新的富比较运算符。

        1:尝试在 python shell 中输入“import this”。

        【讨论】:

          猜你喜欢
          • 2018-05-27
          • 1970-01-01
          • 2018-09-23
          • 1970-01-01
          • 2014-05-09
          • 2016-10-22
          • 2021-07-20
          • 1970-01-01
          • 2020-11-10
          相关资源
          最近更新 更多