Почему параметр cmp удален из sort/sorted в Python3.0?

из вики Python: In Py3.0, the cmp parameter was removed entirely (as part of a larger effort to simplify and unify the language, eliminating the conflict between rich comparisons and the __cmp__ methods).

Я не понимаю, почему cmp удален в py3.0

рассмотрим этот пример:

>>> def numeric_compare(x, y):
        return x - y
>>> sorted([5, 2, 4, 1, 3], cmp=numeric_compare)
[1, 2, 3, 4, 5]

а теперь рассмотрим эту версию (рекомендуется и совместима с 3.0):

def cmp_to_key(mycmp):
    'Convert a cmp= function into a key= function'
    class K(object):
        def __init__(self, obj, *args):
            self.obj = obj
        def __lt__(self, other):
            return mycmp(self.obj, other.obj) < 0
        def __gt__(self, other):
            return mycmp(self.obj, other.obj) > 0
        def __eq__(self, other):
            return mycmp(self.obj, other.obj) == 0
        def __le__(self, other):
            return mycmp(self.obj, other.obj) <= 0
        def __ge__(self, other):
            return mycmp(self.obj, other.obj) >= 0
        def __ne__(self, other):
            return mycmp(self.obj, other.obj) != 0
    return K

>>> sorted([5, 2, 4, 1, 3], key=cmp_to_key(reverse_numeric))
[5, 4, 3, 2, 1]

Последнее очень многословно, а в первом та же цель достигается всего одной строкой. С другой стороны, я пишу свой собственный класс, для которого я хочу написать метод __cmp__. из моего небольшого чтения в Интернете рекомендуется писать __lt__,__gt__,__eq__,__le__,__ge__,__ne__ and not __cmp__ Опять же, почему эта рекомендация? не могу ли я просто определить, что __cmp__ делает жизнь проще?


person brain storm    schedule 25.11.2013    source источник
comment
Вы спрашиваете о двух разных вещах: методе __cmp__ для сравнения классов и аргументе ключевого слова cmp для функций сортировки для настройки сортировки. Конечно, они не совсем не связаны, но это ни в коем случае не одно и то же. Когда вы пишете функцию cmp, которая сравнивает ваши объекты, ей все равно, использует ли она для этого __cmp__ или __lt__; когда вы пишете функцию key, которая создает ключевые значения для ваших объектов, ей все равно, использует ли она для этого __cmp__ или __lt__ (или ни то, ни другое). Итак, какой из двух вопросов вы задаете?   -  person abarnert    schedule 26.11.2013
comment
(На самом деле есть и третья вещь, которую вы можете сбить с толку, это cmp. функция, также удаленная в 3.x.)   -  person abarnert    schedule 26.11.2013
comment
все виды cmp удалены в 3.X или не рекомендуется использовать..правильно?   -  person eagertoLearn    schedule 26.11.2013
comment
@ user2708477: Верно, специальный метод __cmp__ никогда не вызывается, нет параметра cmp ни в одной из функций, связанных с сортировкой, и нет встроенной функции cmp.   -  person abarnert    schedule 26.11.2013
comment
поэтому в основном забудьте cmp и используйте ключи для сортировки; богатые сравнения для класса .. это мое сообщение домой ..   -  person eagertoLearn    schedule 26.11.2013
comment
@user2708477: Точно.   -  person abarnert    schedule 26.11.2013
comment
Хорошее видео о Python и этой теме, я связал время начала части, посвященной сравнению функции, но на самом деле стоит посмотреть все видео… Дайте ему несколько минут… @l4mpi: Да, лучше в качестве комментария.   -  person koffein    schedule 26.11.2013


Ответы (2)


Для двух объектов a и b __cmp__ требует, чтобы одно из a < b, a == b и a > b было истинным. Но это может быть не так: рассмотрим множества, где очень часто ни одно из них не является истинным, например. {1, 2, 3} против {4, 5, 6}.

Итак, __lt__ и друзья были представлены. Но это оставило Python с двумя отдельными механизмами упорядочения, что довольно нелепо, поэтому менее гибкий из них был удален в Python 3.

На самом деле вам не нужно реализовывать все шесть методов сравнения. Вы можете использовать декоратор@total_ordering и реализовать только __lt__ и __eq__.

edit: Также обратите внимание, что в случае сортировки функции key могут быть более эффективными, чем cmp: в приведенном вами примере Python, возможно, придется вызывать вашу функцию сравнения Python O (n²) раз. Но функцию key нужно вызывать только O(n) раз, и если возвращаемое значение является встроенным типом (как это очень часто бывает), попарные сравнения O(n²) проходят через C.

person Eevee    schedule 25.11.2013
comment
когда вы хотите сравнить два набора, например {1,2,3} и {4,5,6}. Это зависит от того, как правильно определить ваш cmp, я могу определить его таким образом, чтобы он возвращал первый элемент набора 1 - первый элемент набора 2. В том же примечании, как определить расширенные методы сравнения, решить, сравнивая два набора, которые вы описали - person brain storm; 26.11.2013
comment
@user1988876: Расширенные методы сравнения решают проблему, потому что __lt__ и __gt__ могут оба возвращать False__le__, и __ge__, и __eq__ также возвращают False, поэтому только __ne__ возвращает True). Это прямо представляет тот факт, что первый набор не меньше, не больше и не равен второму. - person abarnert; 26.11.2013
comment
@ user1988876: Кроме того, этот ответ предполагает, что вы знаете, что делает сравнение наборов в Python, но я подозреваю, что на самом деле вы этого не знаете. a < b для наборов означает, что a является правильным подмножеством b. Отсюда становится очевидным, почему вы должны получать те же результаты, что и для {1, 2, 3} < {4, 5, 6}. - person abarnert; 26.11.2013
comment
@abarnert: Это имеет смысл. Спасибо - person brain storm; 26.11.2013
comment
@user1988876 user1988876 и в этом проблема с __cmp__: вам нужно выбрать один из трех возможных ответов, и ваш первый порыв — придумать что-то, что позволит вам это сделать, но ни один из них не является правильным для эти два набора. (вы не можете сравнивать наборы по их первым элементам, потому что они неупорядочены!) - person Eevee; 26.11.2013
comment
В качестве примечания: когда вы создаете числовые типы, вы можете использовать ABC в numbers чтобы правильно проводить сравнения (наряду со всем остальным). См. модуль fractions со ссылкой на источник. код, например. - person abarnert; 26.11.2013
comment
Сегодня я кое-что узнал о питоне, как всегда, из постов SO! Спасибо вам всем - person eagertoLearn; 26.11.2013
comment
Одна интересная вещь, которую я только что узнал об эквивалентах C-API: хотя весь код в 2.7, который требует сравнений, вызывает функции нового стиля PyObject_RichCompare/PyObject_RichCompareBool, а старые функции PyObject_Cmp/PyObject_Compare больше не используются в версии 3.x, они все еще не объявлены устаревшими в C API версии 2.7. - person abarnert; 26.11.2013
comment
Что-то, что я только что понял (и imho может быть лучшим примером, чем сравнение между непересекающимися множествами): cmp(float('nan'), float('nan')) дает -1, даже если очевидно, что float('nan') < float('nan') равно False - person berdario; 30.11.2013
comment
Мы должны признать, что функция cmp более гибкая, чем функция key, даже если мы можем легко преобразовать нашу cmp в key в 99% случаев, в некоторых случаях мы никогда не сможем сделать это наверняка (не в чистом виде), особенно когда совместимость с кодом C, что имеет значение. в то время как C все еще использует strcmp, Python отбрасывает все типы cmp. - person saeedgnu; 07.02.2016
comment
cmp абсолютно не более гибкий, чем key. существует много видов упорядочения, в том числе некоторые встроенные в язык, которые cmp вообще не могут выразить. - person Eevee; 10.02.2016
comment
Могу ли я также указать, что bool(cmp(a,b)) даст тот же результат, что и a != b, что очень сбивает с толку. - person Tadhg McDonald-Jensen; 22.03.2016
comment
Указание на то, что cmp негибкое, потому что оно не учитывает типы, которые не полностью упорядочены, равносильно утверждению, что вычитание негибкое, потому что вы не можете вычитать списки. По определению, cmp — это функция, которая имеет смысл только для полностью упорядоченных типов. Использование ключевой функции определенно менее гибко: например. вы не можете изменить порядок, используя только ключевую функцию (наивно вы бы попробовали new_key = lambda x: -old_key(x), но это предполагает, что ключи отрицательные). Вот почему каждая функция Python, которая принимает key, имеет аргумент reverse для компенсации неадекватности. - person Rufflewind; 13.12.2016
comment
Не удовлетворены. Я определил свой собственный cmp=, когда знал, что сортирую. Я понимаю аргумент, но я совершенно не согласен с этим направлением. Также рекомендуемый cmp_to_key намного менее эффективен, чем моя лямбда-функция. - person Samantha Atkins; 11.07.2018

cmp был удален, поскольку атрибут key для .sort() и sorted() в большинстве случаев лучше. Это был пережиток C в большей степени, чем что-либо еще, и это сбивало с толку. Необходимость реализации отдельного метода __cmp__ рядом с расширенными операторами сравнения (__lt__, __gt__ и т. д.) сбивала с толку и бесполезна.

Вы всегда можете использовать functools.cmp_to_key() для адаптации существующей функции cmp.

Ваш конкретный пример мог быть реализован без функции key, конечно, поскольку целые числа уже доступны для заказа; просто добавьте reverse=True.

Для пользовательских классов используйте @functools.total_ordering декоратор, чтобы развернуть __eq__ и один метод оператора сравнения (например, __lt__ или __gt__ и т. д.) в полную реализацию упорядочения.

person Martijn Pieters    schedule 25.11.2013
comment
И, в том же духе, functools.total_ordering может быть полезен для второго бита - хотя я всегда думал, что вместо этого он должен жить в модуле classtools :) - person mgilson; 26.11.2013
comment
для пользовательских классов, почему рекомендуется не использовать cmp? но вместо этого __ge__, __lt__ и т.д.. - person brain storm; 26.11.2013
comment
@ user1988876: Помимо ответа, который я дал на тот же вопрос в комментариях к другому ответу, есть тот факт, что __cmp__ устарел и никогда не будет вызываться, если надкласс или подкласс определяет какое-либо из четырех расширенных сравнений (что может привести к некоторые забавные баги) и, конечно же, вообще не работает в 3.x. Кроме того, вы все еще путаете cmp с __cmp__. - person abarnert; 26.11.2013
comment
Это невозможно реализовать без key, потому что он сравнивает наоборот. (В этом случае key может быть просто neg или lambda x: -x.) Что ж, это можно реализовать без key, если вместо этого использовать ключевое слово reverse, но вам нужно одно из двух. - person abarnert; 26.11.2013
comment
@abarnert: ах, я хотел упомянуть «обратное». - person Martijn Pieters; 26.11.2013
comment
@functools.total_ordering больше похож на декоратор класса, чем на декоратор функции. Я не использовал это ранее. так что просто подтверждаю - person brain storm; 26.11.2013
comment
@ user1988876: @functools.total_ordering действительно является декоратором класса. - person Martijn Pieters; 26.11.2013