Подтвердить что ты не робот

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

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

Я заметил (по крайней мере, в п. 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, поэтому я принимаю некоторые унаследованные причины. Еще хотелось бы услышать правильное объяснение, хотя:)

4b9b3361

Ответ 1

Если мы посмотрим на PEP 207 для 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 module:

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

Ответ 2

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

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

Ответ 3

Как сказал hircus, сравнения стиля __cmp__ устарели в пользу богатых операторов (__lt__,...) в Python 3 Первоначально сравнения были реализованы с использованием __cmp__, но существуют некоторые типы/ситуации, когда простого оператора __cmp__ недостаточно (например, экземпляры класса Color могут поддерживать == и !=, но не < > или >), поэтому были добавлены расширенные операторы сравнения, оставив __cmp__ для обратной совместимости. Следуя философии питона "Должен быть один - и желательно только один - простой способ сделать это", 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.