python list排序键功能选择

时间:2016-12-08 19:23:01

标签: python sorting key

我有一个对象列表,并希望根据实例函数的返回值对它们进行排序。有两种方法可以做到这些

l.sort(key=lambda x:x.f())
l.sort(key=methodcaller('f'))

是否比其他方式更好?或者这只是个人偏好?

3 个答案:

答案 0 :(得分:4)

methodcaller('f') 更快,因为它可以在C代码中执行属性查找和方法调用。

lambda增加了以下额外开销:

  • 调用lambda必须退出sort() C循环回Python代码。这需要一个带有相关数据的新帧对象。

  • 查找method属性是一个Python操作码,其开销高于C中的直接等价。

  • 接下来从Python框架调用方法必须再次在Python调用堆栈上推送该框架。 C代码也有一个堆栈,但这要轻得多。

  • 从被调用的方法返回到Python框架,从堆栈中弹出,然后lambda返回,导致功能框架再次被销毁(这仍然是更多工作)

您可以衡量差异:

>>> from timeit import timeit
>>> timeit('m("")', 'm = lambda s: s.lower()', number=10**7)
1.2575681940070353
>>> timeit('m("")', 'from operator import methodcaller; m = methodcaller("lower")', number=10**7)
1.061251598992385

因此,对空字符串的str.lower()进行了700万次调用,methodcaller()的速度提高了约16%。

现在,如果您的所有数据都是完全相同的类型object.f 总是 绑定到同一个方法,那么您可以使用未绑定的方法:

l.sort(key=SharedType.f)

这样可以节省您在每个实例上查找的内容。

答案 1 :(得分:3)

我认为,如果l的所有元素都属于同一类型,那么最好的方法是

class X:
    def __init__(self):
        ...
    def f(self):
        ...

你可以做到

l.sort(key=X.f)

答案 2 :(得分:1)

它们是完全等效的,但methodcaller可能会更快一些:

class Fun(object):
    def __init__(self, value):
        self.value = value

    def f(self):
        return self.value

import random
from operator import methodcaller

l = [Fun(random.random()) for _ in range(10000)]

assert sorted(l, key=lambda x:x.f()) == sorted(l, key=methodcaller('f'))

%timeit sorted(l, key=lambda x:x.f())     # 100 loops, best of 3: 8.4 ms per loop
%timeit sorted(l, key=methodcaller('f'))  # 100 loops, best of 3: 7.5 ms per loop

正如@PatrickHaugh所指出的那样,您可能只使用class.f作为甚至更快的关键功能,但正如@MartijnPieters所说,只有当所有对象都属于{{ 1}}:

class