Понимание функции карты

311
map(function, iterable, ...)

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

Если одна итерация короче другой, предполагается, что она расширена элементами None.

Если функция есть None, предполагается тождественная функция; если есть несколько аргументов, map()возвращает список, состоящий из кортежей, содержащих соответствующие элементы из всех итераций (своего рода операция транспонирования).

Итерируемые аргументы могут быть последовательностью или любым итеративным объектом; Результатом всегда является список.

Какую роль это играет в создании декартового произведения?

content = map(tuple, array)

Какой эффект имеет размещение кортежа где-нибудь там? Я также заметил, что без функции карты вывод abcи с ним, это a, b, c.

Я хочу полностью понять эту функцию. Справочные определения также трудно понять. Слишком много фантазии.

Веб мастер
источник
2
Чего вы на самом деле хотите достичь и почему именно вы хотите использовать map?
Крис Харпер
3
@WebMaster да, согласно первому предложению в вставленной вами документации - «Применить функцию к каждому элементу итерации». Остальная часть абзаца о более сложных случаях - как, map(None, a, b, c)оказывается, делает zip(a, b, c). Но вы очень редко видите это на практике именно потому, что zipвызов эквивалентен.
lvc
9
Я очень стараюсь изучать Python и всякий раз, когда я открываю определение в python.org. после первого предложения я ничего не понимаю. Хорошо. Спасибо.
Веб-мастер
2
tupleявляется функцией (ну, это более нюанс, чем это, но она ведет себя как функция), которая принимает итеративный, и дает вам кортеж с теми же элементами - так tuple([1, 2, 3])что эквивалентно (1, 2, 3). Ибо map(tuple, array), arrayбудет итерируемым итерируемым (представьте список списков), и он возвращает каждый внутренний список, превращенный в кортеж.
LVC
1
В общем, это первое предложение документации любой функции, которая имеет наибольшее значение. Если вы понимаете это, вы понимаете суть этого. В остальном он подробно описывает поведение, и некоторые из них будут немного непрозрачными для начала, и вам, возможно, придется столкнуться со странной идиомой, основанной на этом, прежде чем вы увидите, «о, вот что это значит!». Но как только вы получите этот момент для нескольких встроенных лампочек, вам станет легче понимать документы.
LVC

Ответы:

442

mapне особенно питонический. Я бы порекомендовал использовать списочные выражения:

map(f, iterable)

в основном эквивалентно:

[f(x) for x in iterable]

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

[(a, b) for a in iterable_a for b in iterable_b]

Синтаксис немного сбивает с толку - это в основном эквивалентно:

result = []
for a in iterable_a:
    for b in iterable_b:
        result.append((a, b))
Дейв
источник
36
Я нахожу использование mapгораздо менее многословным, чем списочные, по крайней мере для случая, который вы демонстрируете.
Марбель
1
Как я могу использовать карту для свойств? Что такое map-эквивалент [v.__name__ for v in (object, str)]?
A Sz
@ASz Как насчет map(lambda v: v.__name__, list)?
Килиан
10
Карта работает быстрее, поскольку она не вызывает функции, основанные на длине итераторов. Вызов функций имеет накладные расходы. Смотрите 6:00 youtube.com/watch?v=SiXyyOA6RZg&t=813s
anati
1
@anati я думал , mapбыло иногда быстрее , чем постижения, иногда нет, именно из - за функции накладных расходов вызова? В частности, эвристика, которую я выучил, заключается в том, что при использовании mapтребует, чтобы вы вводили дополнительный вызов функции, понимание быстрее? Например, меня заставили поверить, что map(lambda foo: foo.bar, my_list)это медленнее, чем foo.bar for foo in my_listдаже map(operator.add, my_list_of_pairs)медленнее x + y for x, y in my_list_of_pairs, именно из-за вызова дополнительной функции.
mtraceur
86

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

map в Python 3 это эквивалентно:

def map(func, iterable):
    for i in iterable:
        yield func(i)

и единственное отличие в Python 2 состоит в том, что он создаст полный список результатов, которые будут возвращаться сразу, а не в yielding.

Хотя соглашение Python обычно предпочитает списочные выражения (или выражения-генераторы) для достижения того же результата, что и при вызове map, особенно если вы используете лямбда-выражение в качестве первого аргумента:

[func(i) for i in iterable]

В качестве примера того, что вы просили в комментариях к вопросу - «превратить строку в массив», для «массива» вы, вероятно, хотите либо кортеж, либо список (оба они ведут себя немного как массивы из других языков) -

 >>> a = "hello, world"
 >>> list(a)
['h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd']
>>> tuple(a)
('h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd')

Использование mapздесь будет, если вы начнете со списка строк вместо одной строки - mapможете перечислить их все по отдельности:

>>> a = ["foo", "bar", "baz"]
>>> list(map(list, a))
[['f', 'o', 'o'], ['b', 'a', 'r'], ['b', 'a', 'z']]

Обратите внимание, что map(list, a)это эквивалентно в Python 2, но в Python 3 вам нужен listвызов, если вы хотите сделать что-то кроме подачи в forцикл (или функция обработки, такая как sumэта, нуждается только в итерации, а не в последовательности). Но также отметьте еще раз, что понимание списка обычно предпочтительнее:

>>> [list(b) for b in a]
[['f', 'o', 'o'], ['b', 'a', 'r'], ['b', 'a', 'z']]
LVC
источник
map (fun x -> (x, x)) не кажется трудным для понимания ... (хотя вытащить истинный декартовой продукт из карты было бы невозможно, все, что создает карта, всегда является какой-то формой списка)
Кристофер Мичински
36

map создает новый список, применяя функцию к каждому элементу источника:

xs = [1, 2, 3]

# all of those are equivalent — the output is [2, 4, 6]
# 1. map
ys = map(lambda x: x * 2, xs)
# 2. list comprehension
ys = [x * 2 for x in xs]
# 3. explicit loop
ys = []
for x in xs:
    ys.append(x * 2)

n-ary mapэквивалентно объединению входных итераций и последующему применению функции преобразования к каждому элементу этого промежуточного сжатого списка. Это не декартово произведение:

xs = [1, 2, 3]
ys = [2, 4, 6]

def f(x, y):
    return (x * 2, y // 2)

# output: [(2, 1), (4, 2), (6, 3)]
# 1. map
zs = map(f, xs, ys)
# 2. list comp
zs = [f(x, y) for x, y in zip(xs, ys)]
# 3. explicit loop
zs = []
for x, y in zip(xs, ys):
    zs.append(f(x, y))

Я использовал zipздесь, но mapповедение на самом деле немного отличается, когда итерируемые объекты не имеют одинаковый размер - как отмечено в его документации, они расширяют итерируемые для содержания None.

Cat Plus Plus
источник
1
сложно, пытаясь переварить этот пост
Web Master
1
@WebMaster Что в этом сложного?
Джосси Кальдерон
Лучший ответ на мой взгляд. Использование лямбды в примере в качестве функции делает это очень понятным.
Шелдонзи
К сожалению, все они не эквивалентны - выходные данные предназначены [2,4,6]для понимания списка и явных циклов, но карта возвращает объект карты - например, я получаю следующее: <map at 0x123a49978>что я тогда должен привести в список.
leerssej
20

Если немного упростить, вы можете представить себе map()что-то вроде этого:

def mymap(func, lst):
    result = []
    for e in lst:
        result.append(func(e))
    return result

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

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

Для второй части вопроса: какую роль это играет в создании декартового произведения? ну, map() может быть использован для генерации декартового произведения списка вроде этого:

lst = [1, 2, 3, 4, 5]

from operator import add
reduce(add, map(lambda i: map(lambda j: (i, j), lst), lst))

... Но, по правде говоря, использование product()является гораздо более простым и естественным способом решения проблемы:

from itertools import product
list(product(lst, lst))

В любом случае, результатом является декартово произведение, lstкак определено выше:

[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5),
 (2, 1), (2, 2), (2, 3), (2, 4), (2, 5),
 (3, 1), (3, 2), (3, 3), (3, 4), (3, 5),
 (4, 1), (4, 2), (4, 3), (4, 4), (4, 5),
 (5, 1), (5, 2), (5, 3), (5, 4), (5, 5)]
Оскар Лопес
источник
17

map()Функция есть , чтобы применить ту же процедуру для каждого элемента в Iterable структуры данных, как списки, генераторы, строки и другие вещи.

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

Представьте, что у вас есть функция, которая берет число, добавляет 1 к этому числу и возвращает его:

def add_one(num):
  new_num = num + 1
  return new_num

У вас также есть список номеров:

my_list = [1, 3, 6, 7, 8, 10]

если вы хотите увеличить каждое число в списке, вы можете сделать следующее:

>>> map(add_one, my_list)
[2, 4, 7, 8, 9, 11]

Примечание: как минимум map()необходимо два аргумента. Сначала имя функции, а затем что-то вроде списка.

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

У нас есть три списка:

list_one = [1, 2, 3, 4, 5]
list_two = [11, 12, 13, 14, 15]
list_three = [21, 22, 23, 24, 25]

map() может сделать вас новым списком, который содержит добавление элементов по определенному индексу.

Теперь запомните map(), нужна функция. На этот раз мы будем использовать встроенную sum()функцию. Бег map()дает следующий результат:

>>> map(sum, list_one, list_two, list_three)
[33, 36, 39, 42, 45]

ПОМНИТЕ:
В Python 2 map()будет проходить итерацию (проходить по элементам списков) в соответствии с самым длинным списком и переходить Noneк функции для более коротких списков, поэтому ваша функция должна их искать Noneи обрабатывать, иначе вы получите ошибки. В Python 3 map()остановлюсь после окончания самого короткого списка. Кроме того, в Python 3 map()возвращает итератор, а не список.

BlooB
источник
8

Python3 - карта (функциональная, повторяемая)

Одна вещь, которая не была упомянута полностью (хотя @BlooB вроде упомянул об этом), это то, что карта возвращает объект карты, а не список. Это большая разница, когда дело доходит до производительности при инициализации и итерации. Рассмотрим эти два теста.

import time
def test1(iterable):
    a = time.clock()
    map(str, iterable)
    a = time.clock() - a

    b = time.clock()
    [ str(x) for x in iterable ]
    b = time.clock() - b

    print(a,b)


def test2(iterable):
    a = time.clock()
    [ x for x in map(str, iterable)]
    a = time.clock() - a

    b = time.clock()
    [ str(x) for x in iterable ]
    b = time.clock() - b

    print(a,b)


test1(range(2000000))  # Prints ~1.7e-5s   ~8s
test2(range(2000000))  # Prints ~9s        ~8s

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

Ранга
источник