Как стать автором
Обновить

Комментарии 25

Упустили как минимум generator expressions и обычные генераторы (с yield).

как вообще можно сравнивать list comprehension и около генераторное выражение обернутое в лист? или сравнивайте корректно строго на 2.7 или вместо list comprehension используйте генератор обернутый в лист. а то получается попытка сравнить несравнимое и впихнуть невпихуемое.
Если Вам не требуются все вычисленные значения сразу (а может и вообще не потребуются), то Вам стоит остановить свой выбор на map.

Непонятно, почему вы сравниваете с list expressions, а не с generator expressions.

Питонячий dis даёт разницу в инструкции CALL_FUNCTION. Она отсутсвует в вашем примере
[x*x for x in values]
. Добавьте её туда для более корректного сравнения

$ python -m timeit -r 10 -s "a = lambda x: x*x" "list(map(a, range(1000000)))" 
$ python -m timeit -r 10 -s "a = lambda x: x*x" "[a(x) for x in range(1000000)]" 


List comprehension удобнее тем, что фильтрацию удобно вставлять

[a(x) for x in range(1000000) if x%2 == 0]
list(map(a, filter(lambda x: x%2 == 0, range(1000000))))


Также generator-comprehension generator-expression можно делать подобным образом.

(x*x for x in range(10))
Также generator-comprehension можно делать подобным образом.
(x*x for x in range(10))
Терминологически несколько неоднозначно его обозвали.
То что вы написали generator expression, a не comprehension. Разница принципиальная, те генераторы которые comprehensions (list, set, dictionary) пишутся в квадратных скобках и выдают всю коллекцию, а те которые generator expression (как в вашем примере) — по одному элементу при каждом обращении.
Спасибо! Как раз хотел сказать, что list comprehension читабельнее и проще для понимания на мой личный вкус. Поэтому я почти всегда за него. В статье по ссылке это тоже есть.
Питон не тот язык где уместны такие «оптимизации», питон для тех случаев когда читаемостью кода жертвовать нельзя, именно поэтому списковые включения нужно использовать всегда и это официальная рекомендация, что кстати намекает что автор что-то неправильно замерял — уверен что списковые включения как минимум не медленнее чем map.
если учитывать, что map в 3м питоне превратился в генератор, то разница между ними все таки ощутимая. асимтоматика перебора значений так и останется линейной в обоих случаях, а вот по памяти и скорости инициализации любой генератор утрет нос list comprehension'y.
Гуидо не согласен:
In addition, the list comprehension executes much faster than the solution using map and lambda. This is because calling a lambda function creates a new stack frame while the expression in the list comprehension is evaluated without creating a new stack frame.
с этим я спорить не собираюсь, в условиях что мы хотим сделать что-то простое как x+1. однако это не отменяет сказанного мной: генератор иниализируется быстрее полного построения готового списка, памяти требует примерно ноль, асимтоматика перебора значений линейная в обоих случаях. однако, если проводить честные замеры, то в list comprehension мы обязаны вызывать какую-нибудь функцию, не потому, что так надо, а банально для чистоты эксперимента тк вызов функции одна из самых дорогих операций и просто взять ее, выкинуть и сказать что генераторы отстой — это как сказать что запорожец с реактивным двигателем быстрее болида формулы 1.
Так это о другом — конечно не надо строить весь список если его можно не строить, например не нужен доступ по индексу, тогда можно юзать generator expression зачем map?
предлагаю закрыть тему тк дело не в том как я или Вы смотрите на эти 2 сильно разные вещи, а в том, о чем в статье не сказано при проведении сравнений и замеров: чем они отличаются и какие плюсы и минусы у них есть. а самое главное не сказано: выбирайте инструмент исходя из задачи )
map — это не совсем генератор. У него нет, например, метода send.

Это питон. Тут надо биться не за производительность*, а за простоту и понятность кода.


  • Разумеется, не охреневая до O(n²) вместо O(n) или сжирания кучи памяти на ровном месте.
Если мне память не изменяет, то разработчики питона давно рекомендуют использовать list comprehensions вместо map, filter и прочего. По крайней мере при мирграции на третий питон утилитка 2to3 старалась заменять map & filter на них.
Автор ниасилил мануал. Даже статью написал, хы.
помню костылял сверхбыстрый файлменеджер для старой нокиии на симбе, было весело. В результате — список из 800+ файлов и папок, причем сперва папки, все в алфавитном порядке и за 0.2 секунды.
С приходом Python 3.8 и PEP 572, пользы от генераторов и list/set/dict comprehension стало куда больше, нежели от map/filter.
Даже интересно стало, что за книжки такие, которые кричат об обязательности использования map? Гвидо ван Россум называет map ошибкой и призывает использовать вместо него включения. Дэвид Бизли недавно написал «I can't even remember the last time I used map. 1999? It was a long time ago to be sure.»

Статья в духе "я продолжаю начинать изучать программирование". Автор копнул глубоко — в дизассемблер байткода, и в то же время безумно поверхностно:


  1. Не рассмотрены generator expressions
  2. Выбор между map и list/generator expression определяется в первую очередь сложностью обработки каждого элемента. Одно дело, если каждый элемент надо возвести в квадрат, и совсем другое дело, если для каждого элемента нужно сходить в базу, потом в сторонний сервис, а ещё это залогировать
ваш п.2 порождает классические O(N^2) алгоритмы, когда на каждый айтем в списке нужно заново делать монструозные действия по одному, связанные с вводом-выводом и задержкой
Откуда здесь O(N^2)?
Чтоб вы знали, нотация O(N^2) имеет вполне конкретный смысл, она не обозначает просто «очень тяжёлые вычисления».

В детстве меня пугали бабайкой. Сейчас пугают O(N^2). Интересно, что не было ни бабайки, ни O(N^2)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории