代码之家  ›  专栏  ›  技术社区  ›  Nick Sergeant

如何根据对象的属性对对象列表进行排序?

  •  615
  • Nick Sergeant  · 技术社区  · 17 年前

    我有一个Python对象列表,我想按每个对象的特定属性进行排序:

    [Tag(name="toe", count=10), Tag(name="leg", count=2), ...]
    

    如何对列表进行排序 .count 按降序排列?

    7 回复  |  直到 6 年前
        1
  •  1587
  •   Dorian Turba niamulbengali    7 年前

    要对列表进行排序,请执行以下操作:

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

    要返回新列表,请使用 sorted :

    new_list = sorted(orig_list, key=lambda x: x.count, reverse=True)
    

    说明:

    • key=lambda x: x.count 按计数排序。
    • reverse=True 按降序排序。

    更多关于 sorting by keys .

        2
  •  100
  •   tzot    8 年前

    一种最快的方法,特别是如果你的列表有很多记录,是使用 operator.attrgetter("count") 然而,这可能会在Python的预运算符版本上运行,因此最好有一个回退机制。那么,您可能希望执行以下操作:

    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
    
        3
  •  79
  •   Steven Rumbalski    13 年前

    读者应该注意到key=方法:

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

    比向对象添加丰富的比较运算符快很多倍。我很惊讶地读到这篇文章(《Python in a Nutshell》第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倍左右。他们说这是因为python中使用了高度优化的排序算法( timsort ).

    尽管如此,.sort(lambda)比普通的旧.sort()更快,这很奇怪。我希望他们能解决这个问题。

        4
  •  71
  •   jpp    8 年前

    面向对象的方法

    如果适用,最好将对象排序逻辑作为类的属性,而不是包含在需要排序的每个实例中。

    这确保了一致性,并消除了对样板代码的需求。

    至少,您应该指定 __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]
    
        5
  •  40
  •   attrgetter attrgetter    17 年前
    from operator import attrgetter
    ut.sort(key = attrgetter('count'), reverse = True)
    
        6
  •  16
  •   muhuk    17 年前

    它看起来很像Django ORM模型实例的列表。

    为什么不按以下方式对它们进行排序:

    ut = Tag.objects.order_by('-count')
    
        7
  •  11
  •   rob    17 年前

    如果要排序的属性是 财产 ,则可以避免导入 operator.attrgetter 并使用该物业的 fget 相反,方法。

    例如,对于一个类 Circle 拥有房产 radius 我们可以对以下列表进行排序 circles 按如下半径:

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

    这不是最著名的功能,但通常会为我节省一行导入时间。

        8
  •  7
  •   Georgy rassa45    6 年前

    向对象类添加丰富的比较运算符,然后使用列表的sort()方法。
    看见 rich comparison in python .


    更新 :虽然这种方法可行,但我认为Triptych的解决方案更适合您的情况,因为它更简单。

    推荐文章