TLDR: понимание каждый раз оценивает все свое выражение. map
оценивает свое выражение один раз и каждый раз применяет результат .
Выражение, передаваемое в map
и значения, обрабатываются по-разному. Важные части того, что делают map
и понимания, можно перевести следующим образом:
def map(func: 'callable', iterable):
for item in iterable:
yield func(item)
def comprehension(expr: 'code', iterable):
for item in iterable:
yield eval(expr)
Важное отличие состоит в том, что map
уже получает функцию , тогда как comprehension
получает выражение .
Теперь lambda x:x
- это выражение, которое оценивает как функцию.
>>> co = compile('lambda x: x', '<stackoverflow>', 'eval')
>>> co
<code object <module> at 0x105d1c810, file "<stackoverflow>", line 1>
>>> eval(co)
<function __main__.<lambda>(x)>
В частности, оценивая выражение для получения функции - это действие, которое требует времени.
Поскольку map
и понимания ожидают разных вещей, lambda
передается им по-разному. То, что делает интерпретатор, может быть расширено с помощью явного eval
/ compile
:
>>> list(map(eval(compile('lambda x: x', '<stackoverflow>', 'eval')), range(10)))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(comprehension(compile('(lambda x: x)(item)', '<stackoverflow>', 'eval'), range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Важное отличие должно быть теперь ясно:
- Для
map
выражение оценивается один раз , прежде чем оно будет передано в map
. Для каждого элемента map
вызывает результирующую функцию. - Для
comprehension
выражение передается как неоцененное. Для каждого элемента comprehension
создает функцию , затем вызывает результирующую функцию.
Важно отметить, что map
не всегда быстрее, чем понимание. map
выигрывает от создания / поиска функции один раз. Однако вызовы функций являются дорогостоящими, и для понимания не требуется использовать функцию.
Как правило, если выражение использует только имена, локальные для понимания, оно выполняется быстрее.
In [302]: %timeit list(map(lambda i:i,range(100000)))
...: %timeit [i for i in range(100000)]
8.86 ms ± 38.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
4.49 ms ± 35.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Если само выражение имеет для динамического выполнения дорогостоящей операции, map
не приносит пользы.
In [302]: %timeit list(map(lambda i: (lambda x:x)(i),range(100000)))
...: %timeit [(lambda x:x)(i) for i in range(100000)]
19.7 ms ± 39.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
15.9 ms ± 43.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)