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.