21

主要是好奇。

我注意到(至少在 py 2.6 和 2.7 中) afloat具有所有熟悉的丰富比较功能:__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 中按预期工作,所以我假设一些遗留原因。不过还是想听听正确的解释;)

4

3 回答 3

21

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

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

所以似乎在 2.x 中对整数比较进行了优化。如果我们看一下源代码,我们会发现:

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模块获得它们:

>>> import operator
>>> operator.gt(2,1)
True
于 2012-05-30T05:09:59.020 回答
5

__cmp__()是进行比较的老式方式,并且不推荐使用仅在 Python 2.1 中引入的丰富运算符__lt__等)。__le__从 2.7.x 开始,转换可能还没有完成——而在 Python 3.x__cmp__中完全删除了。

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

于 2012-05-30T04:29:46.490 回答
1

正如 hircus 所说,在 Python 3 中,__cmp__样式比较已被弃用,取而代之的是丰富的运算符( __lt__, ...)。最初,比较是使用 实现的__cmp__,但在某些类型/情况下,简单的__cmp__运算符是不够的(例如颜色类可以支持==and !=,但不支持<or >),因此添加了丰富的比较运算符,__cmp__以保持向后兼容性。遵循“应该有一种——最好只有一种——明显的方式来做到这一点”的 Python 哲学1,在 Python 3 中删除了遗留支持,此时可能会牺牲向后兼容性。

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

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

于 2012-05-30T05:15:01.463 回答