Python: Почему у класса int нет операторов расширенного сравнения, таких как `__lt__()`?

В основном любопытно.

Я заметил (по крайней мере в 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 ответа

Решение

Если мы посмотрим на PEP 207 для Богатых сравнений, то в конце вот это интересное предложение:

Уже существующая вставка, которая имеет дело с целочисленными сравнениями, все еще будет применяться, что не приведет к снижению производительности в большинстве распространенных случаев.

Так что, похоже, в 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

__cmp__() является старомодным способом проведения сравнений и не рекомендуется в пользу богатых операторов (__lt__, __le__ и т.д.), которые были введены только в Python 2.1. Вероятно, переход не был завершен с 2.7.x - тогда как в Python 3.x __cmp__ полностью удален

У Haskell самая элегантная реализация, которую я когда-либо видел - быть Ord (порядковый) тип данных, вам просто нужно определить, как < а также = работает, и сам класс типов предоставляет реализации по умолчанию для <=, > а также >= с точки зрения этих двух (которые вы можете определить сами, если хотите). Вы можете написать такой класс самостоятельно на Python, не зная, почему это не по умолчанию; Вероятно, причины производительности.

Как сказал Гиркус, __cmp__ сравнение стилей не рекомендуется в пользу богатых операторов (__lt__,…) В Python 3. Первоначально сравнения были реализованы с использованием __cmp__, но есть некоторые типы / ситуации, когда простой __cmp__ оператор не достаточно (например, экземпляры класса Color могут поддерживать == а также !=, но нет < или же >), поэтому были добавлены богатые операторы сравнения, оставляя __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: попробуйте ввести "import this" в оболочку python.

Другие вопросы по тегам