При выборе чисто между attrgetter('attributename')
и lambda o: o.attributename
в качестве ключа сортировки, затем использование attrgetter()
является опцией быстрее из двух.
Помните, что функция ключа применяется только один раз к каждому элементу в списке, перед сортировкой, поэтому для сравнения двух мы можем использовать их непосредственно во временном испытании:
>>> 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 раз примерно на 40 мкс быстрее, чем lambda
. Это связано с тем, что вызов функции 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)