我有一个对象列表,并希望根据实例函数的返回值对它们进行排序。有两种方法可以做到这些
l.sort(key=lambda x:x.f())
l.sort(key=methodcaller('f'))
是否比其他方式更好?或者这只是个人偏好?
答案 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