การเรียงลำดับรายการ Python ที่กำหนดเอง


98

ฉันกำลังปรับโครงสร้างโค้ดเก่าของฉันและเจอสิ่งนี้:

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()นำเสนอการจัดเรียงแบบกำหนดเองในลักษณะเดียวกันมากขึ้นโมดูโลความแตกต่างในการเรียกประชุม
Russell Borogove

2
สิ่งที่เกิดขึ้นคือการใช้keyพารามิเตอร์เป็นที่ต้องการมากกว่าการส่งผ่าน cmpฟังก์ชัน (ในภายหลังยังไม่มีการนำไปใช้ใน Python 3)
jsbueno

มันค่อนข้างคลุมเครือขึ้นอยู่กับว่ารายการในรายการคืออะไร รหัสของคุณต้องการให้มีแอตทริบิวต์fooมิฉะนั้นจะระเบิด ดีกว่าที่จะกำหนดที่กำหนดเอง__lt__()วิธีการสำหรับการเรียนของคุณแล้วsorted()และlist.sort()จะทำงานออกจากกล่อง (Btw, วัตถุไม่จำเป็นอีกต่อไปที่จะกำหนด__cmp__()เพียง__lt__(). ดูนี้
smci

คำตอบ:


60

เป็นเอกสารที่นี่

วิธีการ sort () ใช้อาร์กิวเมนต์ที่เป็นทางเลือกสำหรับการควบคุมการเปรียบเทียบ

cmp ระบุฟังก์ชันการเปรียบเทียบแบบกำหนดเองของอาร์กิวเมนต์สองรายการ (รายการ) ซึ่งควรส่งคืนจำนวนลบศูนย์หรือบวกขึ้นอยู่กับว่าอาร์กิวเมนต์แรกถือว่าน้อยกว่าเท่ากับหรือใหญ่กว่าอาร์กิวเมนต์ที่สอง: cmp = lambda x, y : cmp (x.lower (), y.lower ()). ค่าเริ่มต้นคือไม่มี


ขอบคุณ miles82 ฉันกำลังตรวจสอบที่นี่และมองไม่เห็นในลายเซ็นของวิธีdocs.python.org/tutorial/datastructures.html
Lorenzo

ฉันไม่เห็นข้อความเดียวกันในหน้าที่คุณลิงก์ เอกสารมีการเปลี่ยนแปลงหรือไม่ นอกจากนี้เมื่อฉันพยายามที่จะใช้งานฉันได้รับcmp TypeError: 'cmp' is an invalid keyword argument for this functionเกิดขึ้นที่นี่คืออะไร?
HelloGoodbye

2
@HelloGoodbye sort () ไม่ได้มีการโต้แย้ง cmp ในหลาม 3 นี้เป็นคำตอบที่เก่าเมื่อการเชื่อมโยงเอกสารถูกหลาม 2. คุณสามารถค้นหาเอกสารเก่าที่นี่หรืออ่านเพิ่มเติมได้ที่นี่ หากคุณใช้ Python 3 ให้ใช้คีย์อาร์กิวเมนต์แทน
miles82

และถ้าคุณต้องการให้ฟังก์ชันเปรียบเทียบจริง ๆ ล่ะ? ฉันต้องการใช้ตัวเลขในสตริง (ที่มีความยาวเท่าไหร่ก็ได้เลือกออกมาอย่างละโมบ) เป็นสัญลักษณ์เทียบเท่ากับการปฏิบัติต่ออักขระแต่ละตัว ฉันรู้วิธีที่จะบรรลุสิ่งนั้นเล็กน้อยหากฉันอาจจัดเตรียมฟังก์ชันการเปรียบเทียบ แต่ไม่ใช่ถ้าฉันต้องให้ฟังก์ชันหลัก เหตุใดจึงเปลี่ยนไป
HelloGoodbye

ผมคิดว่ามันยังคงสามารถทำได้ถ้าแต่ละหมายเลขที่อยู่ในสตริงจะถูกเข้ารหัสโดยใช้การเข้ารหัสที่ว่าคำสั่งซื้อตัวเลข lexicographically เช่นLevenshtein เข้ารหัส แต่ผมคิดว่านี่เป็นวิธีแก้ปัญหามากขึ้นกับความจริงที่ว่าsortไม่ได้ใช้ฟังก์ชั่นการเปรียบเทียบเป็นอาร์กิวเมนต์ในหลาม 3 และไม่เป็นสิ่งที่ฉันจริงต้องการจะทำ
HelloGoodbye

108

หมายเหตุด้านข้างนี่เป็นทางเลือกที่ดีกว่าในการใช้การเรียงลำดับเดียวกัน:

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

หรืออีกทางหนึ่ง:

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

ลองดูวิธีการเรียงลำดับจะมีประโยชน์มาก


1
TIL เกี่ยวกับตัวดำเนินการมีประโยชน์มาก
ffledgling

16

เช่นเดียวกับตัวอย่างนี้ คุณต้องการจัดเรียงรายการนี้

[('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

จากนั้นแปลงเป็นฟังก์ชันหลัก:

from functools import cmp_to_key
letter_cmp_key = cmp_to_key(letter_cmp))

ตอนนี้คุณสามารถใช้ลำดับการจัดเรียงที่กำหนดเองได้:

[('c', 2), ('b', 2), ('a', 3)].sort(key=letter_cmp_key)

4
จะรู้ได้อย่างไรว่าจะจัดเรียงรายการอะไร?
พระสงฆ์คาเมรอน

2
@CameronMonks yourList.sort (letter_cmp)
kebab-case

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

ฉันรู้ว่าหลายคนได้โพสต์คำตอบที่ดีไว้แล้ว อย่างไรก็ตามฉันต้องการแนะนำวิธีการที่ดีและง่ายโดยไม่ต้องนำเข้าห้องสมุดใด ๆ

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)

ผลลัพธ์จะเป็น

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

ผลลัพธ์จะถูกจัดเรียงตามลำดับของพารามิเตอร์ที่เราให้ไว้ในรูปแบบทูเพิล


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.