lambda vs. operator.attrgetter('xxx')作为Python中的排序键函数

时间:2010-04-24 15:56:38

标签: python lambda

我正在查看一些使用比较函数进行大量排序调用的代码,看起来应该使用关键函数。

如果您要更改seq.sort(lambda x,y: cmp(x.xxx, y.xxx)),则最好:

seq.sort(key=operator.attrgetter('xxx'))

或:

seq.sort(key=lambda a:a.xxx)

我也有兴趣对现有代码进行更改的优点发表意见。

3 个答案:

答案 0 :(得分:21)

“对现有代码进行更改”是程序发展的方式;-)。编写一系列测试,使用现有代码提供已知结果,保存这些结果(在测试环境中通常称为“黄金文件”);然后进行更改,重新运行测试,并验证(理想情况下是以自动方式)测试结果的唯一变化是那些特别预期的变化 - 没有不希望的或意外的一面效果。当然,人们可以使用更复杂的质量保证策略,但这是许多“集成测试”方法的要点。

至于编写简单key=函数的两种方法,设计意图是通过更专业化使operator.attrgetter更快,但至少在当前版本的Python中,速度没有可测量的差异。既然如此,对于这种特殊情况,我会推荐lambda,因为它更简洁一般(而且我通常不是一个lambda爱好者,请注意! - )。

答案 1 :(得分:10)

仅在attrgetter('attributename')lambda o: o.attributename之间选择一个排序键时,则使用attrgetter()是两者的 faster 选项。

请记住,在排序之前,键函数仅对列表中的每个元素应用一次,因此要进行比较,我们可以在时间试用中直接使用它们:

>>> from timeit import Timer
>>> from random import randint
>>> from dataclasses import dataclass, field
>>> @dataclass
... class Foo:
...     bar: int = field(default_factory=lambda: randint(1, 10**6))
...
>>> testdata = [Foo() for _ in range(1000)]
>>> def test_function(objects, key):
...     [key(o) for o in objects]
...
>>> stmt = 't(testdata, key)'
>>> setup = 'from __main__ import test_function as t, testdata; '
>>> tests = {
...     'lambda': setup + 'key=lambda o: o.bar',
...     'attrgetter': setup + 'from operator import attrgetter; key=attrgetter("bar")'
... }
>>> for name, tsetup in tests.items():
...     count, total = Timer(stmt, tsetup).autorange()
...     print(f"{name:>10}: {total / count * 10 ** 6:7.3f} microseconds ({count} repetitions)")
...
    lambda: 130.495 microseconds (2000 repetitions)
attrgetter:  92.850 microseconds (5000 repetitions)

因此,应用attrgetter('bar') 1000次大约比lambda快40μs。这是因为调用 Python 函数具有一定的开销,而不是调用attrgetter()产生的本机函数。

这种速度优势也转化为更快的排序:

>>> def test_function(objects, key):
...     sorted(objects, key=key)
...
>>> for name, tsetup in tests.items():
...     count, total = Timer(stmt, tsetup).autorange()
...     print(f"{name:>10}: {total / count * 10 ** 6:7.3f} microseconds ({count} repetitions)")
...
    lambda: 218.715 microseconds (1000 repetitions)
attrgetter: 169.064 microseconds (2000 repetitions)

答案 2 :(得分:4)

如以前的评论者所述,attrgetter稍快一些,但是在很多情况下,两者之间的差异很小(〜微秒)。

关于可读性,我个人更喜欢lambda,因为它是人们在不同环境中以前见过的结构,因此其他人可能更容易阅读和理解。

另一个警告是,与使用lambda不同,使用attrgetter时,您的IDE应该能够在attr名称上发出错字。

通常,我倾向于选择不需要额外导入的结构,只要该结构足够容易读写。