【问题标题】:Faster way to sort a list of objects based on a attribute in Python基于 Python 中的属性对对象列表进行排序的更快方法
【发布时间】:2015-03-23 07:32:23
【问题描述】:

我有一个 python 中的对象列表,我想根据属性进行排序

例如:

abc 是一个具有 id 和 count 属性的类。

我有一个 abc 类的列表对象。

list=[abc('1',120),abc('2',0),abc('0',180),abc('5',150)].

我想按属性'count'的升序对列表进行排序

我已经使用了:

list.sort(key=attrgetter('count'))

我发现使用分析我的 python 脚本需要大量时间进行排序

任何人都可以提出一种更好和更快的方法来根据属性对对象列表进行排序,从而最大限度地减少排序时间

【问题讨论】:

  • 排序比什么要花更多时间?
  • 比较多的时间?
  • 我相信这意味着它比没有键或比较器功能的排序需要更多时间,正如您从我的回答中看到的那样。
  • 一个想法:尝试将适当的__slots__ 添加到您的abc 类定义中。
  • 通过list.sort(key=..)排序已经很不错了。也许您可以通过减少排序来减少排序时间。

标签: python list python-2.7 sorting


【解决方案1】:

挑剔:您在列表中使用名称 list,这将覆盖标准的 list 类。最好使用l 作为列表名称。

我测试了对包含 12 倍列表内容的列表进行 100000 次排序。当我使用 sorted() 函数来避免重新排序已经排序的列表时,在没有比较器函数或键的情况下花费了 0.848 秒。

我能想到的方法至少有以下三种:

A.将 sort() 与比较器函数一起使用:

def comparator(x, y):
  return cmp(x.count, y.count)
l.sort(cmp=comparator)

当我使用 sorted() 函数来避免重新排序已经排序的列表时,这在我的系统上花费了 9.598 秒。

B.将 sort() 与 key 函数一起使用:

l.sort(key=operator.attrgetter('count'))

当我使用 sorted() 函数来避免重新排序已经排序的列表时,这在我的系统上花费了 3.111 秒。

C.使用本机 C 代码来提高排序的性能。我没有测试这个。

因此,您似乎已经在使用目前最快的全 Python 方式,而前进的道路将是使用本机 C 代码。

【讨论】:

  • 由于list.sort 具有排序列表的副作用,您将主要对排序列表进行排序——这意味着比较器排序所花费的时间将不能代表使用比较器排序的平均成本.尝试改用sorted(l, cmp=comparator) 进行测试。
  • 是的,你是对的,我重新排序了一个已经排序的列表。我使用 sorted() 函数再次尝试,结果现在非常有趣:cmp 变体非常非常慢。
【解决方案2】:

我相信sort方法是使用Timsort算法实现的,所以在排序方面没有太多可以改进的地方。

如果您可以控制代码的插入部分,您可以以不同的方式插入元素。
例如,您可以使用 binary heap 来优化最大元素的检索(请参阅 Python 中的 heapq 模块)或 binary search tree 来维护排序顺序。 您选择的数据结构,主要取决于您要对元素做什么。

【讨论】:

  • 我尝试在python中实现heapq模块,但它没有帮助,因为对象列表不能直接基于属性进行堆化。我必须根据属性对列表进行排序,然后heapify 它并不能解决我的问题,而是让它变得复杂。
  • 您可以在heapq 上实施您自己的解决方案,例如stackoverflow.com/questions/8875706/…
  • 我在插入时使用了二进制搜索插入,这样我就可以消除排序的开销。它奏效了。谢谢你的建议
【解决方案3】:

如果我理解正确的话:

class Abc(object):
    def __init__(self, name, count):
        self.name = name
        self.count = count

    @classmethod
    def sort_key(cls, key):
        if key == 'count':
            return lambda obj: obj.count
        elif key == 'name':
            return lambda obj: obj.name


lst = [Abc('1', 120), Abc('2', 0), Abc('0', 180), Abc('5', 150)]

lst.sort(key=Abc.sort_key('count'))
for e in lst:
    print e.name, e.count
print

lst.sort(key=Abc.sort_key('name'))
for e in lst:
    print e.name, e.count
print

我不建议你使用 'id'、'abc' 和 'list' 作为任意变量的名称,因为它们是 python 中的关键字。

【讨论】:

  • 我将类名命名为“abc”,目的是展示我面临的问题的示例。要求是减少基于属性对对象列表进行排序所花费的时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-30
  • 2021-06-27
  • 2014-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多