Есть ли смысл использовать map() по сравнению с for?

Повторяет ли map() список, как это делает for? Есть ли смысл в использовании map vs for?

Если да, то сейчас мой код выглядит так:

for item in items:
    item.my_func()

Если это имеет смысл, я бы хотел сделать это map(). Это возможно? Как выглядит пример?


person roder    schedule 17.05.2009    source источник


Ответы (8)


Вы можете использовать map вместо цикла for, который вы показали, но поскольку вы не используете результат item.my_func(), это не рекомендуется. map следует использовать, если вы хотите применить функцию без побочных эффектов ко всем элементам списка. Во всех других ситуациях используйте явный цикл for.

Кроме того, начиная с Python 3.0 map возвращает генератор, поэтому в этом случае map не будет вести себя так же (если только вы явно не оцените все элементы, возвращаемые генератором, например, вызвав list на нем).


Изменить: kibibu спрашивает в комментариях пояснение, почему первый аргумент map не должен быть функция с побочными эффектами. Я попробую ответить на этот вопрос:

map предназначен для передачи функции f в математическом смысле. При таких обстоятельствах не имеет значения, в каком порядке f применяется к элементам второго аргумента (конечно, если они возвращаются в исходном порядке). Что еще более важно, в этих обстоятельствах map(g, map(f, l)) семантически эквивалентно map(lambda x: g(f(x)), l), независимо от порядка, в котором f и g применяются к их соответствующим входам.

Например, не имеет значения, возвращает ли map и итератор или полный список сразу. Однако если f и/или g вызывают побочные эффекты, то эта эквивалентность гарантируется только в том случае, если семантика map(g, map(f, l)) такова, что на любом этапе g применяется к первым n элементам, возвращаемым map(f, l), до применения map(f, l) f к (n + 1) элементу l. (Это означает, что map должен выполнять максимально ленивую итерацию — что он делает в Python 3, но не в Python 2!)

Еще один шаг вперед: даже если мы предположим реализацию map в Python 3, семантическая эквивалентность может легко разрушиться, если вывод map(f, l) будет, например, передается через itertools.tee перед передачей во внешний вызов map.

Приведенное выше обсуждение может показаться теоретическим, но по мере того, как программы становятся более сложными, их становится все труднее анализировать и, следовательно, труднее отлаживать. Обеспечение инвариантности некоторых вещей несколько облегчает эту проблему и фактически может предотвратить целый класс ошибок.

Наконец, map напоминает многим людям его действительно функциональный аналог в различных (чисто) функциональных языках. Передача ему «функции» с побочными эффектами запутает этих людей. Поэтому, поскольку альтернативу (т. е. использование явного цикла) реализовать не сложнее, чем вызов map, настоятельно рекомендуется ограничить использование map теми случаями, в которых применяемая функция не вызывает побочных эффектов. .

person Stephan202    schedule 17.05.2009
comment
Почему это не рекомендуется? Это семантика или что-то вроде распределенной обработки (где побочные эффекты нарушают параллелизм)? - person kibibu; 07.04.2010
comment
@kibibu: я начал отвечать на ваш вопрос, но получил слишком много текста для этого поля комментария. Поэтому я обновил ответ :) Как это помогает! (Что касается вашего замечания о распределенной обработке итерируемого: хотя это можно привести в качестве дополнительного аргумента при разговоре о map на других языках, я не думаю, что Python (в настоящее время) выполняет какие-либо оптимизации такого рода. .) - person Stephan202; 08.04.2010

Вы можете написать это, используя карту следующим образом:

map(cls.my_func, items)

заменив cls классом элементов, которые вы повторяете.

Как упомянул Stephan202, в данном случае это не рекомендуется.

Как правило, если вы хотите создать новый список, применяя некоторую функцию к каждому элементу в списке, используйте карту. Это подразумевает, что функция не имеет побочных эффектов, и, таким образом, вы можете (потенциально) запускать карту параллельно.

Если вы не хотите создавать новый список или функция имеет побочные эффекты, используйте цикл for. В вашем примере это так.

person Kiv    schedule 17.05.2009

Есть небольшая семантическая разница, которая, вероятно, закрыта в спецификации языка python. map можно явно распараллелить, а for — только в особых случаях. Код может выйти из for, но только с исключением из map.

На мой взгляд, map не должен также гарантировать порядок применения функций, в то время как for должен. Насколько я знаю, ни одна реализация Python в настоящее время не может выполнять эту автоматическую распараллеливание.

person Pasi Savolainen    schedule 17.05.2009

Вы можете переключить свой map на какую-нибудь крутую многопоточную ИЛИ многопроцессорную ИЛИ распределенную вычислительную среду, если вам нужно. Disco – это пример распределенной, устойчивой к сбоям инфраструктуры на основе erlang и python. Я настроил его на 2 коробки по 8 ядер, и теперь моя программа работает в 16 раз быстрее, благодаря кластеру Disco, однако мне пришлось переписать свою программу из списков и циклов для сопоставления/уменьшения.

То же самое — написать программу с использованием циклов for и списков, а также map/reduce, но когда вам нужно, чтобы она работала в кластере, вы можете сделать это почти бесплатно, если использовали map/reduce. Если Вы этого не сделали, что ж, Вам придется переписать.

Осторожно: насколько я знаю, python 2.x возвращает список вместо итератора из карты. Я слышал, что это можно обойти, используя iter.imap() (хотя никогда не использовал его).

person Paweł Polewicz    schedule 17.05.2009

Используйте явный цикл for, когда вам не нужен список результатов (например, функции с побочными эффектами).

Используйте понимание списка, когда вам нужен список результатов (например, функции, которые возвращают значение, основанное непосредственно на вводе).

Используйте map(), когда пытаетесь убедить пользователей Lisp в том, что Python стоит использовать. ;)

person Kylotan    schedule 18.05.2009

Основное преимущество map заключается в том, что вы хотите получить результат некоторого вычисления для каждого элемента в списке. Например, этот фрагмент удваивает каждое значение в списке:

map(lambda x: x * 2, [1,2,3,4])  #=> [2, 4, 6, 8]

Важно отметить, что map возвращает новый список с результатами. Он не изменяет исходный список на месте.

Чтобы сделать то же самое с for, вам нужно создать пустой список и добавить дополнительную строку в тело for, чтобы добавить результат каждого вычисления в новый список. Версия map более лаконичная и функциональная.

person Jesse Hallett    schedule 17.05.2009
comment
Понимание списков еще более лаконично, например. [x*2 для x в [1,2,3,4]) в вашем примере. - person Kiv; 18.05.2009

Map иногда может быть быстрее для встроенных функций, чем ручное кодирование цикла for. Попробуйте временную карту (str, range (1000000)) и аналогичный цикл for.

person Jack Gray    schedule 18.05.2009

person    schedule
comment
Это работает, но не отвечает на вопрос, почему это лучше, чем использование цикла for (а именно, это не так). - person Kiv; 18.05.2009