Penyortiran daftar Python kustom

98

Saya refactoring beberapa kode lama saya dan menemukan ini:

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

Kode berfungsi (dan saya menulisnya sekitar 3 tahun yang lalu!) Tetapi saya tidak dapat menemukan hal ini didokumentasikan di mana pun di dokumen Python dan semua orang menggunakannya sorted()untuk mengimplementasikan penyortiran khusus. Adakah yang bisa menjelaskan mengapa ini berhasil?

Lorenzo
sumber
sorted()dan sort()menawarkan pengurutan khusus dengan cara yang hampir sama, modulo perbedaan dalam konvensi panggilan.
Russell Borogove
2
Memang, yang terjadi adalah bahwa menggunakan keyparameter lebih disukai daripada meneruskan cmpfungsi. (Nanti bahkan tidak diimplementasikan dalam Python 3)
jsbueno
Ini agak ambigu, tergantung pada item apa yang ada di daftar itu; kode Anda mengharuskan mereka memiliki atribut foo, jika tidak maka akan meledak. Lebih baik menentukan __lt__()metode kustom untuk kelas Anda, kemudian sorted()dan list.sort()akan bekerja di luar kotak. (Btw, objek tidak perlu lagi didefinisikan __cmp__(), cukup __lt__(). Lihat ini
smci

Jawaban:

60

Itu didokumentasikan di sini .

Metode sort () mengambil argumen opsional untuk mengontrol perbandingan.

cmp menentukan fungsi perbandingan kustom dari dua argumen (item daftar) yang harus mengembalikan angka negatif, nol atau positif bergantung pada apakah argumen pertama dianggap lebih kecil dari, sama dengan, atau lebih besar dari argumen kedua: cmp = lambda x, y : cmp (x.lower (), y.lower ()). Nilai defaultnya adalah Tidak Ada.

miles82
sumber
Terima kasih miles82 Saya telah memeriksa di sini dan tidak dapat melihatnya dalam metode tanda tangan docs.python.org/tutorial/datastructures.html
Lorenzo
Saya tidak melihat teks yang sama di halaman yang Anda tautkan. Apakah dokumentasinya berubah. Selain itu, ketika saya mencoba menggunakan cmp, saya mendapatkan TypeError: 'cmp' is an invalid keyword argument for this function. Apa yang terjadi disini?
HelloGoodbye
2
@HelloGoodbye sort () tidak memiliki argumen cmp di Python 3. Ini adalah jawaban lama ketika tautan dokumen adalah untuk Python 2. Anda dapat menemukan dokumen lama di sini atau membaca lebih lanjut di sini . Jika Anda menggunakan Python 3, gunakan argumen kunci sebagai gantinya.
miles82
Dan bagaimana jika Anda benar-benar ingin memberikan fungsi perbandingan? Saya ingin memperlakukan angka dalam string (dengan panjang berapa pun, dipilih dengan rakus) sebagai simbol, setara dengan bagaimana karakter individu diperlakukan. Saya tahu bagaimana mencapainya dengan mudah jika saya dapat memberikan fungsi perbandingan, tetapi tidak jika saya harus menyediakan fungsi kunci. Mengapa ini berubah?
HelloGoodbye
Saya kira itu masih bisa dicapai jika setiap nomor yang terdapat dalam string dikodekan menggunakan pengkodean yang mengurutkan nomor secara leksikografis, seperti pengkodean Levenshtein . Tapi saya menganggap ini lebih sebagai solusi untuk fakta yang sorttidak mengambil fungsi perbandingan sebagai argumen di Python 3, dan bukan sebagai sesuatu yang sebenarnya ingin saya lakukan.
HelloGoodbye
108

Sebagai catatan tambahan, berikut adalah alternatif yang lebih baik untuk menerapkan penyortiran yang sama:

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

Atau sebagai alternatif:

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

Lihat Cara Menyortir , ini sangat berguna.

Andrew Clark
sumber
1
TIL tentang operator, sangat berguna.
ffledgling
16

Seperti contoh ini. Anda ingin mengurutkan daftar ini.

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

keluaran:

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

Anda harus mengurutkan tupel berdasarkan item kedua, lalu yang pertama:

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

Kemudian ubah menjadi fungsi kunci:

from functools import cmp_to_key
letter_cmp_key = cmp_to_key(letter_cmp))

Sekarang Anda dapat menggunakan tata urutan kustom Anda:

[('c', 2), ('b', 2), ('a', 3)].sort(key=letter_cmp_key)
RryLee
sumber
4
Bagaimana cara mengetahui daftar yang harus diurutkan?
Cameron Monks
2
@Cameronks yourList.sort (letter_cmp)
kebab-case
7

Ini tidak berfungsi di Python 3.

Anda dapat menggunakan functools cmp_to_key agar fungsi perbandingan gaya lama berfungsi.

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)
Kucing Unfun
sumber
1

Saya tahu banyak yang telah memposting beberapa jawaban yang bagus. Namun saya ingin menyarankan satu metode yang bagus dan mudah tanpa mengimpor perpustakaan apa pun.

l = [(2, 3), (3, 4), (2, 4)]
l.sort(key = lambda x: (-x[0], -x[1]) )
print(l)
l.sort(key = lambda x: (x[0], -x[1]) )
print(l)

Outputnya akan

[(3, 4), (2, 4), (2, 3)]
[(2, 4), (2, 3), (3, 4)]

Outputnya akan diurutkan berdasarkan urutan parameter yang kami sediakan dalam format tuple

peddiashrith.dll
sumber
0

Bahkan lebih baik:

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

Diambil dari: https://docs.python.org/3/howto/sorting.html

Steven
sumber