Власне сортування списку Python


93

Я переробляв якийсь свій старий код і натрапив на це:

alist.sort(cmp_items)

def cmp_items(a, b):
    if a.foo > b.foo:
        return 1
    elif a.foo == b.foo:
        return 0
    else:
        return -1

Код працює (і я написав його 3 роки тому!), Але я не можу знайти цю річ, задокументовану де-небудь у документах Python, і всі використовують sorted()для реалізації користувацького сортування. Хтось може пояснити, чому це працює?


sorted()і sort()пропонуємо власну сортування приблизно таким же чином, модульно різницю в умові викликів.
Рассел Борогов

2
Дійсно, трапляється так, що використання keyпараметра є кращим, ніж передача cmpфункції. (Пізніше навіть не реалізовано в Python 3)
jsbueno

Це наче неоднозначно, залежить від того, якими були елементи у списку; ваш код вимагає, щоб вони мали атрибут foo, інакше він підірветься. Краще визначити власний __lt__()метод для вашого класу, тоді sorted()і list.sort()буде працювати нестандартно. (До речі, об'єкти більше не потрібно визначати __cmp__(), просто __lt__(). Дивіться це
smci

Відповіді:


60

Це документально тут .

Метод sort () бере необов’язкові аргументи для контролю порівнянь.

cmp визначає спеціальну функцію порівняння двох аргументів (елементів списку), яка повинна повертати від'ємне, нульове або додатне число залежно від того, чи вважається перший аргумент меншим, рівним або більшим за другий аргумент: cmp = лямбда x, y : cmp (x.lower (), y.lower ()). Значення за замовчуванням - None.


Дякую miles82 Я перевіряв тут і не міг побачити його в підписі методу docs.python.org/tutorial/datastructures.html
Лоренцо

Я не бачу однакового тексту на сторінці, на яку ви посилалися. Чи змінилася документація. Крім того, коли я намагаюся використовувати cmp, я отримую TypeError: 'cmp' is an invalid keyword argument for this function. Що тут відбувається?
HelloGoodbye

1
@HelloGoodbye sort () не має аргументу cmp у Python 3. Це стара відповідь, коли посилання на документи було для Python 2. Ви можете знайти старі документи тут або прочитати більше про це тут . Якщо ви використовуєте Python 3, використовуйте замість цього аргумент key .
милі82

А що, якщо ви дійсно хочете надати функцію порівняння? Я хочу розглядати числа в рядку (будь-якої довжини, вибрані з жадібністю) як символи, еквівалентно тому, як в іншому випадку поводяться з окремими символами. Я знаю, як досягти цього тривіально, якщо можу надати функцію порівняння, але не якщо мені потрібно надати ключову функцію. Чому це було змінено?
HelloGoodbye

Я думаю, це все ще може бути досягнуто, якщо кожне число, що міститься у рядку, кодується з використанням кодування, яке упорядковує числа лексикографічно, наприклад, кодування Левенштейна . Але я розглядаю це скоріше як обхід того факту, sortякий не приймає функцію порівняння як аргумент у Python 3, а не як те, що я насправді хотів би зробити.
HelloGoodbye

104

Як зауваження, ось краща альтернатива для здійснення того ж сортування:

alist.sort(key=lambda x: x.foo)

Або як варіант:

import operator
alist.sort(key=operator.attrgetter('foo'))

Перегляньте сортування , як це дуже корисно.


1
TIL про оператора, дуже корисний.
перекинуто

13

Так само, як цей приклад. Ви хочете відсортувати цей список.

[('c', 2), ('b', 2), ('a', 3)]

вихід:

[('a', 3), ('b', 2), ('c', 2)]

вам слід відсортувати кортежі за другим елементом, а потім за першим:

def letter_cmp(a, b):
    if a[1] > b[1]:
        return -1
    elif a[1] == b[1]:
        if a[0] > b[0]:
            return 1
        else:
            return -1
    else:
        return 1

Нарешті:

просто sort(letter_cmp)


4
Звідки воно знає, який список сортувати?
Кемерон Монкс,

2
@CameronMonks yourList.sort (letter_cmp)
Minyc510

7

У Python 3 це не працює.

Ви можете використовувати functools cmp_to_key, щоб функції порівняння в старому стилі працювали.

from functools import cmp_to_key

def cmp_items(a, b):
    if a.foo > b.foo:
        return 1
    elif a.foo == b.foo:
        return 0
    else:
        return -1

cmp_items_py3 = cmp_to_key(cmp_items)

alist.sort(cmp_items_py3)

1

У python3 ви можете написати:

from functools import cmp_to_key

def cmp_items(a, b):
    if a.foo > b.foo:
        return 1
    elif a.foo == b.foo:
        return 0
    else:
        return -1

alist = sorted(alist, key=cmp_to_key(cmp_items))

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.