كيفية فرز قائمة الكائنات بناءً على سمة الكائنات؟
سؤال
لدي قائمة بكائنات بايثون التي أرغب في فرزها حسب سمة للكائنات نفسها.تبدو القائمة كما يلي:
>>> ut
[<Tag: 128>, <Tag: 2008>, <Tag: <>, <Tag: actionscript>, <Tag: addresses>,
<Tag: aes>, <Tag: ajax> ...]
كل كائن له عدد:
>>> ut[1].count
1L
أحتاج إلى فرز القائمة حسب عدد الأعداد التنازلية.
لقد رأيت عدة طرق لذلك، ولكني أبحث عن أفضل الممارسات في بايثون.
المحلول
# 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)
نصائح أخرى
وهناك طريقة يمكن أن يكون أسرع، وخاصة إذا قائمتك لديها الكثير من السجلات، هو استخدام operator.attrgetter("count")
. ومع ذلك، وهذا قد يعمل على نسخة ما قبل مشغل بايثون، لذلك سيكون من الجميل أن يكون آلية للتراجع. قد ترغب في القيام بما يلي، ثم:
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
يجب أن يلاحظ القراء أن المفتاح = الطريقة:
ut.sort(key=lambda x: x.count, reverse=True)
أسرع بعدة مرات من إضافة عوامل المقارنة الغنية إلى الكائنات.لقد فوجئت بقراءة هذا (صفحة 485 من "بايثون باختصار").يمكنك تأكيد ذلك عن طريق إجراء الاختبارات على هذا البرنامج الصغير:
#!/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(lambda) أسرع من .sort() القديم العادي.آمل أن يصلحوا ذلك.
from operator import attrgetter
ut.sort(key = attrgetter('count'), reverse = True)
النهج الموجه للكائنات
من الممارسات الجيدة جعل منطق فرز الكائنات، إن أمكن، خاصية للفئة بدلاً من دمجها في كل حالة مطلوب فيها الترتيب.
وهذا يضمن الاتساق ويزيل الحاجة إلى التعليمات البرمجية المعيارية.
كحد أدنى، يجب عليك تحديد __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]
ويبدو مثل الكثير من لائحة الحالات نموذج جانغو مكتب إدارة السجلات.
لماذا لا فرزها في الاستعلام مثل هذا:
ut = Tag.objects.order_by('-count')
إضافة عوامل المقارنة الغنية إلى فئة الكائن، ثم استخدام نوع الأسلوب () من القائمة.
انظر مقارنة الغنية في بيثون .
على تحديث : في على الرغم من أن هذه الطريقة ستعمل، وأعتقد أن الحل من ثلاثية الجوانب هو أكثر ملاءمة لحالتك لأن أبسط طريقة
.