Есть ли значение в использовании map () против? - PullRequest
10 голосов
/ 17 мая 2009

Карта () выполняет итерацию по списку как "for"? Есть ли значение в использовании карты против?

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

for item in items:
    item.my_func()

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

Ответы [ 8 ]

23 голосов
/ 17 мая 2009

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

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


Edit : 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) st l. (Это означает, что map должен выполнять самую ленивую итерацию, что и в Python 3, но не в Python 2!)

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

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

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

5 голосов
/ 18 мая 2009

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

map(cls.my_func, items)

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

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

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

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

2 голосов
/ 18 мая 2009

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

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

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

2 голосов
/ 18 мая 2009

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

Это то же самое дело, написать программу, использующую для циклов и списков, а также для отображения / уменьшения, но когда вам нужно, чтобы она работала в кластере, вы можете сделать это почти бесплатно, если использовали карту / уменьшение. Если Вы этого не сделали, вам придется переписать.

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

2 голосов
/ 18 мая 2009

Существует небольшое семантическое различие, которое, вероятно, закрыто в спецификации языка Python. Карта явно распараллеливается, тогда как для только в особых ситуациях. Код может прервать из для , но только с исключением из map .

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

1 голос
/ 17 мая 2009

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

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

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

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

0 голосов
/ 19 мая 2009

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

0 голосов
/ 17 мая 2009
map(lambda item: item.my_func(), items)
...