วิธีการจัดเรียงรายการวัตถุตามคุณลักษณะของวัตถุ?


804

ฉันมีรายการของวัตถุ Python ที่ฉันต้องการเรียงลำดับตามคุณสมบัติของวัตถุนั้น ๆ รายการดูเหมือนว่า:

>>> ut
[<Tag: 128>, <Tag: 2008>, <Tag: <>, <Tag: actionscript>, <Tag: addresses>,
 <Tag: aes>, <Tag: ajax> ...]

แต่ละวัตถุมีจำนวน:

>>> ut[1].count
1L

ฉันต้องการเรียงลำดับรายการตามจำนวนการนับจากมากไปน้อย

ฉันได้เห็นวิธีการหลายอย่างนี้แล้ว แต่ฉันกำลังมองหาแนวปฏิบัติที่ดีที่สุดใน Python



1
การเรียงลำดับวิธีการสำหรับผู้ที่กำลังมองหาข้อมูลเพิ่มเติมเกี่ยวกับการเรียงลำดับใน Python
Jeyekomon

1
นอกเหนือจากโอเปอร์เรเตอร์ .attrgetter ('attribute_name') คุณยังสามารถใช้ functors เป็นคีย์เช่น object_list.sort (key = my_sorting_functor ('my_key')) โดยไม่ทำให้เกิดการใช้งานโดยเจตนา
vijay shanker

คำตอบ:


1313
# To sort the list in place...
ut.sort(key=lambda x: x.count, reverse=True)

# To return a new list, use the sorted() built-in function...
newlist = sorted(ut, key=lambda x: x.count, reverse=True)

เพิ่มเติมเกี่ยวกับการเรียงลำดับตามคีย์


1
ไม่มีปัญหา. btw ถ้า muhuk พูดถูกและเป็นรายการของวัตถุ Django คุณควรพิจารณาวิธีแก้ปัญหาของเขา อย่างไรก็ตามสำหรับกรณีทั่วไปของการเรียงลำดับวัตถุวิธีแก้ปัญหาของฉันน่าจะเป็นแนวปฏิบัติที่ดีที่สุด
Triptych

43
ในรายการขนาดใหญ่คุณจะได้รับประสิทธิภาพที่ดีขึ้นโดยใช้ operator.attrgetter ('count') เป็นกุญแจของคุณ นี่เป็นเพียงฟังก์ชั่นแลมบ์ดา (ระดับล่าง) ที่ได้รับการปรับให้เหมาะสมในคำตอบนี้
David Eyk

4
ขอบคุณสำหรับคำตอบที่ดี ในกรณีที่เป็นรายการพจนานุกรมและ 'count' เป็นหนึ่งในคีย์ของมันจำเป็นต้องเปลี่ยนดังนี้: ut.sort (key = lambda x: x ['count'], reverse = True)
dganesh2002

ฉันคิดว่ามันสมควรได้รับการอัปเดตต่อไปนี้: หากมีความต้องการที่จะเรียงลำดับตามหลายฟิลด์ก็สามารถทำได้โดยการเรียงลำดับการโทร () ต่อเนื่องเนื่องจากไพ ธ อนใช้อัลกอริธึมการเรียงแบบเสถียร
zzz777

86

operator.attrgetter("count")วิธีที่สามารถเป็นที่เร็วที่สุดโดยเฉพาะอย่างยิ่งถ้ารายการของคุณมีจำนวนมากของการบันทึกคือการใช้งาน อย่างไรก็ตามสิ่งนี้อาจทำงานกับ Python เวอร์ชัน pre-operator ดังนั้นจึงเป็นการดีที่จะมีกลไกทางเลือก คุณอาจต้องการทำสิ่งต่อไปนี้จากนั้น:

try: import operator
except ImportError: keyfun= lambda x: x.count # use a lambda if no operator module
else: keyfun= operator.attrgetter("count") # use operator since it's faster than lambda

ut.sort(key=keyfun, reverse=True) # sort in-place

7
ที่นี่ฉันจะใช้ชื่อตัวแปร "keyfun" แทน "cmpfun" เพื่อหลีกเลี่ยงความสับสน การเรียงลำดับ () วิธีการยอมรับฟังก์ชั่นการเปรียบเทียบผ่านอาร์กิวเมนต์ cmp = เช่นกัน
akaihola

ดูเหมือนว่ามันจะไม่ทำงานถ้าวัตถุนั้นมีการเพิ่มคุณสมบัติแบบไดนามิก (ถ้าคุณทำself.__dict__ = {'some':'dict'}ตาม__init__วิธีการแล้ว) ฉันไม่รู้ว่าทำไมมันถึงแตกต่าง
tutuca

@tutuca: __dict__ฉันไม่เคยแทนที่อินสแตนซ์ โปรดทราบว่า "วัตถุที่มีการเพิ่มแอตทริบิวต์แบบไดนามิก" และ "การตั้งค่า__dict__คุณลักษณะของวัตถุ" เป็นแนวคิดแบบตั้งฉากเกือบ ฉันกำลังบอกว่าเพราะความคิดเห็นของคุณดูเหมือนจะบอกเป็นนัยว่าการตั้งค่า__dict__คุณลักษณะนั้นเป็นข้อกำหนดสำหรับการเพิ่มแอตทริบิวต์แบบไดนามิก
tzot

@tzot: ฉันกำลังมองหาที่นี่: github.com/stochastic-technologies/goatfish/blob/master/…และใช้ตัววนซ้ำที่นี่: github.com/TallerTechnologies/dishey/blob/master/app.py#L28 raises ข้อผิดพลาดคุณลักษณะ อาจเป็นเพราะ python3 แต่ก็ยัง ...
tutuca

1
@tzot: ถ้าฉันเข้าใจการใช้operator.attrgetterฉันสามารถจัดหาฟังก์ชั่นที่มีชื่อคุณสมบัติใด ๆ และส่งกลับคอลเลกชันเรียง
IAbstract

64

ผู้อ่านควรสังเกตว่า key = method:

ut.sort(key=lambda x: x.count, reverse=True)

เร็วกว่าการเพิ่มตัวดำเนินการเปรียบเทียบที่เปรียบเทียบให้กับวัตถุหลายครั้ง ฉันประหลาดใจที่อ่านสิ่งนี้ (หน้า 485 ของ "Python ในแบบสั้น") คุณสามารถยืนยันสิ่งนี้ได้ด้วยการรันการทดสอบในโปรแกรมเล็ก ๆ นี้:

#!/usr/bin/env python
import random

class C:
    def __init__(self,count):
        self.count = count

    def __cmp__(self,other):
        return cmp(self.count,other.count)

longList = [C(random.random()) for i in xrange(1000000)] #about 6.1 secs
longList2 = longList[:]

longList.sort() #about 52 - 6.1 = 46 secs
longList2.sort(key = lambda c: c.count) #about 9 - 6.1 = 3 secs

การทดสอบน้อยที่สุดของฉันแสดงว่าการเรียงลำดับแรกช้ากว่า 10 เท่า แต่หนังสือบอกว่าโดยทั่วไปจะช้ากว่าปกติประมาณ 5 เท่า เหตุผลที่พวกเขากล่าวว่าเป็นเพราะอัลกอริทึมการเรียงลำดับที่ดีที่สุดที่ใช้ในหลาม ( timsort )

ยังคงเป็นเรื่องแปลกมากที่. sort (แลมบ์ดา) จะเร็วกว่าเก่า. sort () ธรรมดา ฉันหวังว่าพวกเขาจะแก้ไข


1
การนิยาม__cmp__นั้นเทียบเท่ากับการโทร.sort(cmp=lambda)ไม่ใช่.sort(key=lambda)ดังนั้นจึงไม่แปลกเลย
tzot

@tzot ถูกต้องแน่นอน การเรียงลำดับแรกจะต้องเปรียบเทียบวัตถุกับอีกครั้งและอีกครั้ง การเรียงลำดับที่สองเข้าถึงแต่ละวัตถุเพียงครั้งเดียวเพื่อแยกค่าการนับของมันจากนั้นจะทำการเรียงลำดับตัวเลขอย่างง่ายซึ่งได้รับการปรับให้เหมาะสมที่สุด longList2.sort(cmp = cmp)การเปรียบเทียบเป็นธรรมมากขึ้นจะ .sort()ฉันพยายามออกมานี้และจะดำเนินการเกือบเดียวกับ (เช่น: โปรดทราบว่าพารามิเตอร์การเรียง "cmp" ถูกลบใน Python 3)
Bryan Roach

43

วิธีการเชิงวัตถุ

เป็นวิธีปฏิบัติที่ดีในการจัดทำตรรกะการเรียงลำดับวัตถุหากเหมาะสมคุณสมบัติของคลาสแทนที่จะรวมอยู่ในแต่ละอินสแตนซ์จำเป็นต้องมีการสั่งซื้อ

สิ่งนี้ทำให้มั่นใจได้ถึงความสอดคล้องและไม่จำเป็นต้องใช้รหัสสำเร็จรูป

อย่างน้อยที่สุดคุณควรระบุ__eq__และ__lt__ดำเนินการเพื่อให้สิ่งนี้ทำงานได้ sorted(list_of_objects)จากนั้นเพียงแค่ใช้

class Card(object):

    def __init__(self, rank, suit):
        self.rank = rank
        self.suit = suit

    def __eq__(self, other):
        return self.rank == other.rank and self.suit == other.suit

    def __lt__(self, other):
        return self.rank < other.rank

hand = [Card(10, 'H'), Card(2, 'h'), Card(12, 'h'), Card(13, 'h'), Card(14, 'h')]
hand_order = [c.rank for c in hand]  # [10, 2, 12, 13, 14]

hand_sorted = sorted(hand)
hand_sorted_order = [c.rank for c in hand_sorted]  # [2, 10, 12, 13, 14]

1
นั่นคือสิ่งที่ฉันกำลังมองหา! คุณช่วยชี้ให้เราดูเอกสารบางอย่างที่อธิบายเกี่ยวกับสาเหตุ__eq__และ__lt__ข้อกำหนดการปฏิบัติขั้นต่ำได้อย่างไร
FriendFX

1
@FriendFX ผมเชื่อว่ามันเป็นโดยนัยนี้ :•The sort routines are guaranteed to use __lt__() when making comparisons between two objects...
jpp

2
@FriendFX: ดูportingguide.readthedocs.io/en/latest/comparisons.htmlสำหรับการเปรียบเทียบและการเรียงลำดับ
Cornel Masson

37
from operator import attrgetter
ut.sort(key = attrgetter('count'), reverse = True)

16

ดูเหมือนว่าจะเป็นรายการของอินสแตนซ์ของ Django ORM

ทำไมไม่จัดเรียงตามคำถามแบบนี้:

ut = Tag.objects.order_by('-count')

มันเป็น แต่ใช้ django-tagging ดังนั้นฉันจึงใช้ built-in สำหรับการคว้าชุด Tag โดยการใช้งานสำหรับชุดสืบค้นเฉพาะเช่น: Tag.objects.usage_for_queryset (QuerySet, counts = True)
Nick Sergeant

11

เพิ่มโอเปอเรเตอร์การเปรียบเทียบที่หลากหลายให้กับคลาสอ็อบเจ็กต์จากนั้นใช้เมธอด sort () ของรายการ
ดูการเปรียบเทียบที่อุดมไปด้วยในหลาม


อัปเดต : แม้ว่าวิธีนี้จะใช้งานได้ แต่ฉันคิดว่าโซลูชันจาก Triptych เหมาะสมกับกรณีของคุณมากกว่าเพราะวิธีที่ง่ายกว่า


3

หากคุณสมบัติที่คุณต้องการเรียงลำดับเป็นคุณสมบัติคุณสามารถหลีกเลี่ยงการนำเข้าoperator.attrgetterและใช้fgetวิธีการของคุณสมบัติแทน

ตัวอย่างเช่นสำหรับคลาสที่Circleมีคุณสมบัติradiusเราสามารถเรียงลำดับรายการcirclesตามรัศมีได้ดังนี้:

result = sorted(circles, key=Circle.radius.fget)

นี่ไม่ใช่คุณสมบัติที่รู้จักกันดีที่สุด แต่มักจะช่วยฉันด้วยการนำเข้า

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