46

I was curious if there was any indication of which of operator.itemgetter(0) or lambda x:x[0] is better to use, specifically in sorted() as the key keyword argument as that's the use that springs to mind first. Are there any known performance differences? Are there any PEP related preferences or guidance on the matter?

Endophage
  • 21,038
  • 13
  • 59
  • 90
  • 1
    I would advise to use `operator.itemgetter(0)` – oleg Jun 21 '13 at 20:19
  • even over the standard readability? isn't this sort of performance-based decision making premature optimization? – hexparrot Jun 21 '13 at 20:21
  • 1
    @hexparrot it's such a small code difference, and I don't think readability is better one way or the other. I'm just wondering for the sake of integrating whichever is "better" into my regular lexicon. – Endophage Jun 21 '13 at 20:23
  • 1
    Possible duplicate of [Why should I use operator.itemgetter(x) instead of \[x\]?](http://stackoverflow.com/questions/11287207/why-should-i-use-operator-itemgetterx-instead-of-x) – Chris_Rands Jan 11 '17 at 09:13

3 Answers3

47

The performance of itemgetter is slightly better:

>>> f1 = lambda: sorted(w, key=lambda x: x[1])
>>> f2 = lambda: sorted(w, key=itemgetter(1))
>>> timeit(f1)
21.33667682500527
>>> timeit(f2)
16.99106214600033
Charlie
  • 8,530
  • 2
  • 55
  • 53
michaelmeyer
  • 7,985
  • 7
  • 30
  • 36
  • 3
    But here what is being measured is the creation of the lambda functions. To measure the sorting itself the `timeit` calls should be `timeit(f1())` and `timeit(f2())`. Or am I missing something here? – dojuba Jul 11 '17 at 08:18
  • 2
    @dojuba `timeit.timeit` calls its argument if it is not a string. –  Feb 20 '18 at 07:56
  • yeah, but who writes anything in python that needs to be fast – CpILL Oct 01 '20 at 01:57
22

Leaving aside the speed issue, which is often based on where you make the itemgetter or lambda function, I personally find that itemgetter is really nice for getting multiple items at once: for example, itemgetter(0, 4, 3, 9, 19, 20) will create a function that returns a tuple of the items at the specified indices of the listlike object passed to it. To do that with a lambda, you'd need lambda x:x[0], x[4], x[3], x[9], x[19], x[20], which is a lot clunkier. (And then some packages such as numpy have advanced indexing, which works a lot like itemgetter() except built in to normal bracket notation.)

dreftymac
  • 31,404
  • 26
  • 119
  • 182
JAB
  • 20,783
  • 6
  • 71
  • 80
14

According to my benchmark on a list of 1000 tuples, using itemgetter is almost twice as quick as the plain lambda method. The following is my code:

In [1]: a = list(range(1000))

In [2]: b = list(range(1000))

In [3]: import random

In [4]: random.shuffle(a)

In [5]: random.shuffle(b)

In [6]: c = list(zip(a, b))

In [7]: %timeit c.sort(key=lambda x: x[1])
81.4 µs ± 433 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [8]: random.shuffle(c)

In [9]: from operator import itemgetter

In [10]: %timeit c.sort(key=itemgetter(1))
47 µs ± 202 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

I have also tested the performance (run time in µs) of this two method for various list size.

+-----------+--------+------------+
| List size | lambda | itemgetter |
+-----------+--------+------------+
| 100       | 8.19   | 5.09       |
+-----------+--------+------------+
| 1000      | 81.4   | 47         |
+-----------+--------+------------+
| 10000     | 855    | 498        |
+-----------+--------+------------+
| 100000    | 14600  | 10100      |
+-----------+--------+------------+
| 1000000   | 172000 | 131000     |
+-----------+--------+------------+

enter image description here

(The code producing the above image can be found here)

Combined with the conciseness to select multiple elements from a list, itemgetter is clearly the winner to use in sort method.

jdhao
  • 24,001
  • 18
  • 134
  • 273