Python:为什么int类没有像`__lt __()`那样丰富的比较运算符?

时间:2012-05-30 04:19:13

标签: python language-design language-history

非常好奇。

我注意到(至少在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中按预期工作,所以我假设有一些遗留原因。仍然希望听到正确的解释;)

3 个答案:

答案 0 :(得分:21)

如果我们查看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

答案 1 :(得分:5)

__cmp__()是一种老式的比较方式,deprecated in favor of the rich operators__lt____le__等)仅在Python 2.1中引入。可能从2.7.x开始,转换不完整 - 而在Python 3.x __cmp__中完全删除了。

Haskell拥有我见过的最优雅的实现 - 成为Ord(序数)数据类型,您只需要定义<=的工作方式,以及类型本身为<=>>=提供了这两者的默认实现(如果你愿意,你可以自己定义自己)。您可以在Python中自己编写这样的类,不确定为什么不是默认类;可能是表现原因。

答案 2 :(得分:1)

正如hircus所说,Python中的__cmp__样式比较是deprecated in favor of the rich operators__lt__,...)。最初,比较是使用__cmp__实现的,但也有一些简单__cmp__运算符不足的类型/情况(例如,Color类的实例可以支持==!=,但不支持<> ),因此添加了丰富的比较运算符,留下__cmp__以便向后兼容。遵循python哲学“应该有一个 - 最好只有一个 - 显而易见的方式”, 1 在Python 3中删除了遗留支持,这样可以牺牲向后兼容性。 / p>

在Python 2中,尽管int仍然使用__cmp__以便不破坏向后兼容性,但并非所有浮点数都小于,大于或等于其他浮点数(例如{ {1}}评估为(float('nan') < 0.0, float('nan') == 0.0, float('nan') > 0.0),那么(False, False, False)应返回什么?),因此float('nan').__cmp__(0.0)需要使用较新的丰富比较运算符。

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