Classificação de lista Python personalizada


93

Eu estava refatorando um código antigo meu e me deparei com isto:

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

O código funciona (e eu o escrevi há cerca de 3 anos!), Mas não consigo encontrar isso documentado em nenhum lugar nos documentos do Python e todo mundo usa sorted()para implementar classificação personalizada. Alguém pode explicar por que isso funciona?


sorted()e sort()oferecem classificação personalizada da mesma maneira, modulo a diferença na convenção de chamada.
Russell Borogove

2
Na verdade, o que acontece é que usar um keyparâmetro é preferível a passar uma cmpfunção. (O último nem mesmo é implementado em Python 3)
jsbueno

É meio ambíguo, depende de quais são os itens da lista; seu código requer que eles tenham um atributo foo, caso contrário, ele explode. Melhor para definir um personalizado __lt__()método para a sua classe, em seguida, sorted()e list.sort()vai funcionar out-of-the-box. (A propósito, os objetos não precisam mais ser definidos __cmp__(), apenas __lt__(). Veja isto
smci

Respostas:


60

Está documentado aqui .

O método sort () aceita argumentos opcionais para controlar as comparações.

cmp especifica uma função de comparação personalizada de dois argumentos (itens de lista) que deve retornar um número negativo, zero ou positivo, dependendo se o primeiro argumento é considerado menor, igual ou maior que o segundo argumento: cmp = lambda x, y : cmp (x.lower (), y.lower ()). O valor padrão é nenhum.


Obrigado miles82 Eu estava verificando aqui e não consegui ver na assinatura do método docs.python.org/tutorial/datastructures.html
Lorenzo

Não vejo o mesmo texto na página que você vinculou. A documentação mudou. Além disso, quando tento usar cmp, consigo TypeError: 'cmp' is an invalid keyword argument for this function. O que está acontecendo aqui?
HelloGoodbye

1
@HelloGoodbye sort () não tem um argumento cmp no Python 3. Essa é uma resposta antiga quando o link de documentos era para Python 2. Você pode encontrar os documentos antigos aqui ou ler mais sobre isso aqui . Se você estiver usando Python 3, use o argumento chave .
miles82 de

E se você realmente quiser fornecer uma função de comparação? Quero tratar os números em uma string (de qualquer comprimento, escolhida avidamente) como símbolos, de forma equivalente a como os caracteres individuais são tratados de outra forma. Sei como fazer isso trivialmente se puder fornecer uma função de comparação, mas não se tiver de fornecer uma função-chave. Por que isso foi alterado?
HelloGoodbye

Acho que isso ainda pode ser alcançado se cada número contido na string for codificado usando uma codificação que ordena os números lexicograficamente, como a codificação de Levenshtein . Mas considero isso mais como uma solução alternativa para o fato de que sortuma função de comparação não é considerada como argumento no Python 3, e não como algo que eu realmente gostaria de fazer.
HelloGoodbye

104

Como observação lateral, aqui está uma alternativa melhor para implementar a mesma classificação:

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

Ou alternativamente:

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

Confira o Guia de Classificação , é muito útil.


1
TIL sobre o operador, muito útil.
disparado em

13

Assim como este exemplo. Você quer classificar esta lista.

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

resultado:

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

você deve classificar as tuplas pelo segundo item, depois o primeiro:

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

Finalmente:

somente sort(letter_cmp)


4
Como ele sabe qual lista classificar?
Cameron Monks

2
@CameronMonks yourList.sort (letter_cmp)
Minyc510

7

Isso não funciona no Python 3.

Você pode usar functools cmp_to_key para que as funções de comparação do estilo antigo funcionem.

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

Em python3, você pode escrever:

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))

0

Melhor ainda:

student_tuples = [
    ('john', 'A', 15),
    ('jane', 'B', 12),
    ('dave', 'B', 10),
]

sorted(student_tuples, key=lambda student: student[2])   # sort by age
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

Retirado de: https://docs.python.org/3/howto/sorting.html

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.