Функция map в Python: обработка списка
map применяет функцию к каждому элементу списка одной строкой. Разбираем ленивый итератор, обёртку list() и правило выбора между map и циклом.
Редакция Питоники
Применить правило к каждому элементу списка — самая частая работа с данными: цены умножить на курс, слова привести к верхнему регистру, числа возвести в квадрат. Вы умеете писать это циклом с аккумулятором из урока про счётчики и аккумуляторы — и повторяете тот же каркас каждый раз. Встроенная функция map делает эту работу одной строкой: применяет ваше правило к каждому элементу и отдаёт результаты списком.
Что делает функция map в Python: коротко
map применяет переданную функцию к каждому элементу набора и отдаёт итератор: map(правило, список). Результаты собираются списком через list(map(...)). Функцию-правило передают именем без скобок или короткой lambda прямо на месте вызова. Печатать сам map нельзя — получится служебная запись с адресом памяти.
Знакомый цикл
Задача: удвоить каждую цену. Решение циклом занимает четыре строки каркаса:
prices = [100, 250, 80]
doubled = []
for price in prices:
doubled.append(price * 2)
print(doubled)
[200, 500, 160]
Каркас один и тот же для любой задачи: пустой список, цикл, append результата, печать. Меняется только правило — price * 2. Именно это правило map и принимает на вход.
map: правило и список
map вызывает правило для каждого элемента и собирает результаты:
prices = [100, 250, 80]
def double(n):
return n * 2
doubled = list(map(double, prices))
print(doubled)
[200, 500, 160]
Разбираем вызов map(double, prices): первый аргумент — функция-правило, переданная именем без скобок, как в уроке про lambda: map сам будет её вызывать; второй — список. map применяет функцию к каждому элементу списка, а list собирает результаты в новый список. Double отработала для 100, потом 250, потом 80 — итог совпал с циклом, четыре строки свёрнулись в одну.
map ленивый: сначала итератор
Важная деталь: map не считает сразу. Он возвращает итератор — заготовку, которая знает правило и список, но применяет их только когда результаты запрошены. Запрос — это обёртка list():
numbers = [1, 2, 3]
def square(n):
return n * n
prepared = map(square, numbers)
collected = list(prepared)
print(collected)
[1, 4, 9]
В prepared лежит не список квадратов, а итератор: сама по себе эта строка ничего не вычислила. Всё посчиталось в момент list(prepared). Поэтому правило курса: результат map сразу оборачивайте в list — тогда у вас в руках обычный список, с которым можно печатать, считать и сохранять.
Ленивость видна и в другом свойстве: итератор одноразовый. Он выдаёт элементы один раз — повторная сборка вернёт пустой список:
numbers = [1, 2, 3]
def square(n):
return n * n
squares = map(square, numbers)
print(list(squares))
print(list(squares))
[1, 4, 9] []
Первый list(squares) вытянул из итератора все три квадрата. Второй нашёл его пустым — элементы уже выданы, заново map их не считает. Если результаты нужны дважды — соберите список один раз и сохраните в переменную.
# Так не делают: печать итератора вместо его сборки
prices = [100, 250]
print(map(lambda n: n * 2, prices))
# Так не запускается: у map нет индексов
prices = [100, 250]
doubled = map(lambda n: n * 2, prices)
print(doubled[0])
Правило может быть любой функцией
Первым аргументом map принимает любую функцию — в том числе встроенную. Правило «возьми длину» уже написано за вас:
words = ['кот', 'слон', 'жираф']
lengths = list(map(len, words))
print(lengths)
[3, 4, 5]
len передана именем, без скобок: map сам вызовет её для каждого слова. А когда готовой функции нет, правило пишется lambda прямо на месте вызова — короткое, одноразовое:
rubles = [100, 250, 80]
euros = list(map(lambda r: r / 2, rubles))
print(euros)
[50.0, 125.0, 40.0]
Связка map + lambda — рабочая лошадка обработки списков: правило объявляется и используется в одной строке, отдельная def для разового пересчёта не нужна. Для повторно используемых правил — например, конвертации с курсом из урока про return — честнее именованная функция:
Собираем: конвертация цен
def ruble_to_euro(rubles):
return rubles / 100
prices = [1000, 2500, 800]
euros = list(map(ruble_to_euro, prices))
print('Цены в евро:', euros)
print('Дороже всего:', max(euros))
Цены в евро: [10.0, 25.0, 8.0] Дороже всего: 25.0
Функция-правило возвращает результат, map разворачивает её по списку, list собирает, max находит максимум — детали из разных уроков сложились в одну программу из четырёх строк.
map или цикл: как выбирать
| map | Цикл for | |
|---|---|---|
| Одно простое правило для каждого элемента | Да — одна строка | Можно, но длиннее |
| Условия, пропуски, несколько строк на элемент | Не поместится — lambda одновыраженческая | Да — тело цикла свободное |
| Счётчики и аккумуляторы | Нет — map не считает итоги | Да — total += ... в теле |
| Читаемость | Привыкшему глазу — короче | Понятна с первых уроков |
map покрывает правило «применить ко всем». Частая соседняя задача — «оставить только подходящие»: положительные цены, слова короче пяти букв. Для неё у Python есть функция filter, близнец map — им посвящён следующий урок. А после filter правило встретится снова: sorted с key сортирует список по вашей lambda.
map с двумя списками
У map есть версия, о которой в учебниках пишут через запятую и потому её пропускают: первым аргументом можно передать несколько списков, и правило получит по одному элементу из каждого. Так считают, например, стоимость позиций заказа: цена из одного списка и количество из другого.
def line_total(price, count):
return price * count
prices = [100, 250, 80]
counts = [2, 1, 5]
print(list(map(line_total, prices, counts)))
[200, 250, 400]
Правило получило три пары: (100, 2), (250, 1), (80, 5) — и вернуло три результата. Длины списков при этом не обязаны совпадать: map остановится на самом коротком и лишние элементы просто не попадут в результат.
print(list(map(lambda a, b: a + b, [1, 2, 3], [10, 20])))
[11, 22]
Тройка из первого списка осталась без пары — и потерялась молча, без ошибки. Это стоит держать в голове: map с двумя списками не предупреждает о расхождении длин. Если данные приходят из разных источников, проверьте длины до вызова, а не после.
Ленивость на практике: считаем, не сохраняя
Главная выгода ленивого map в том, что он не хранит результаты. Он отдаёт их по одному и забывает — а значит, можно считать по большому набору, не создавая для этого список в памяти.
numbers = range(1000)
total = sum(map(lambda n: n * 2, numbers))
print(total)
999000
Сумма посчитана по тысяче элементов, но ни один из них не пережил следующий. Замените sum на list(...) — и в памяти появится список из тысячи элементов. На диапазоне в миллион это десятки мегабайт, которые просто не нужны ради одного числа. Мы держим правило: если результат нужен целиком — оборачиваем в list, если нужен один показатель — считаем на лету.
map — это короткая запись генераторного выражения
Под капотом map — тот же генератор, который можно написать руками. Сравните две строки:
def double(n):
return n * 2
nums = [1, 2, 3]
print(list(map(double, nums)))
print([double(n) for n in nums])
[2, 4, 6] [2, 4, 6]
Результаты одинаковые, отличается только форма. Разница в читаемости: когда правило сложное, его удобнее написать именованной функцией и передать в map, — тогда в скобках остаётся короткое map(double, nums). Когда правило простое, генераторное выражение читается ничуть не хуже, а иногда понятнее: видно сразу, что это перебор.
map по словарю
Словарь тоже можно разобрать на элементы и прогнать через map. Метод items() даёт пары «ключ — значение», и правило получает каждую пару целиком.
def line(item):
name, price = item
return f'{name} — {price} руб.'
prices = {'Хлеб': 100, 'Молоко': 250, 'Сыр': 80}
print(list(map(line, prices.items())))
['Хлеб — 100 руб.', 'Молоко — 250 руб.', 'Сыр — 80 руб.']
Порядок строк совпадает с порядком добавления ключей — словарь в Python сохраняет порядок, поэтому вывод предсказуем. Та же схема работает для keys() и values(), если правилу нужен только один элемент пары.
Правилом может быть и метод
У встроенных методов та же сигнатура, что у обычной функции: одно значение на вход, одно на выход. Поэтому их тоже можно передавать в map как правило — отдельно объявлять функцию не нужно.
words = [' кот ', ' собака ', ' ёж']
clean = list(map(str.strip, words))
print(clean)
print(list(map(str.title, clean)))
['кот', 'собака', 'ёж'] ['Кот', 'Собака', 'Ёж']
Здесь метод передали именем — str.strip, без скобок, — ровно так же, как любую функцию. Если понадобится правило посложнее, пишем def и передаём её тем же способом.
Сначала предскажи ответ в голове — это главный навык программиста.
def f(n):
return n + 1
print(list(map(f, [1, 2])))
nums = [1, 2]
m = map(lambda n: n * n, nums)
print(list(m))
print(list(m))
print(list(map(len, ['ок', 'дом'])))
1. Что делает map(f, xs)?
2. Что возвращает сам вызов map(f, xs) без обёртки list()?
3. Почему нельзя печатать map напрямую — print(map(f, xs))?
4. Что напечатает print(list(map(lambda n: n + 1, [1, 2])))?
5. m = map(f, xs). Что вернёт второй вызов list(m) после первого?
6. Может ли правилом для map быть встроенная функция?
7. Что напечатает print(list(map(lambda a, b: a + b, [1, 2, 3], [10, 20])))?
8. Чем map отличается от генераторного выражения?
9. Как проверить, что map действительно ленивый?
Дан список слов words = ['кот', 'собака', 'ёж']. Напиши функцию censor(word), которая возвращает слово с первой буквой и звёздочками вместо остальных: первая буква плюс '*' * (len(word) - 1). Собери через list(map(censor, words)) список hidden и напечатай его.
Что делает функция map в Python?
Применяет переданную функцию к каждому элементу списка: map(double, prices) вызовет double для каждой цены. Результаты собираются списком через list(...): map возвращает ленивый итератор, а не готовый список.
Урок 8: filterПочему print(map(...)) показывает <map object at 0x...>?
map ленивый: без запроса он ничего не вычисляет и печатается как служебная запись итератора с адресом памяти. Результат сразу оборачивают в list: print(list(map(f, xs))) напечатает список результатов.
Как использовать map с lambda?
Передать lambda первым аргументом: list(map(lambda r: r / 2, rubles)) — правило объявляется прямо на месте вызова. Для повторно используемых правил честнее именованная def, переданная именем без скобок.
map быстрее цикла for?
Смысл map не в скорости, а в краткости: одно простое правило на каждый элемент пишется одной строкой. Как только появляются условия, счётчики или несколько действий на элемент — обычный цикл читабельнее и свободнее.
Можно ли применить map сразу к двум спискам?
Да: map(line_total, prices, counts) вызовет правило с парой элементов — по одному из каждого списка. Так считают стоимость позиций заказа: цена из одного списка, количество из другого.
Что будет, если списки для map разной длины?
map возьмёт кратчайший список и лишние элементы отбросит — без ошибки и без предупреждения. Перед вызовом проверяйте длины: len(a) == len(b). Иначе потерянные данные обнаружатся позже, уже в готовом отчёте.
Как map экономит память?
Никаких промежуточных списков: sum(map(f, xs)) считает, отдавая результаты по одному. На диапазоне в миллион элементов list(map(...)) занял бы десятки мегабайт, а сумма — нисколько. Обёртка list(...) нужна только когда результат требуется целиком.
Можно ли использовать map со словарём?
Да, через items(): map(line, prices.items()) передаёт правилу каждую пару «ключ — значение» целиком. Порядок строк в результате совпадает с порядком добавления ключей — словарь в Python сохраняет порядок.
Понравился урок? Сошлитесь на него
«map применяет функцию к каждому элементу списка, а list собирает результаты в новый список.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Функции · Урок 6
Lambda в Python: функция в одну строку
lambda — правило без имени: аргументы до двоеточия, одно выражение после. Учимся писать короткие правила и передавать их в другие функции аргументом.
Функции · Урок 8
filter в Python: оставить подходящие
map менял каждый элемент, а filter выбирает: оставить только чётные, только длинные слова, только заполненные строки. Разбираем filter и его хитрости.
Циклы · Урок 11
Счётчики и аккумуляторы в циклах Python
Сумма чека, число совпадений, лучший результат: переменная, объявленная до цикла и копящая значения внутри, — приём старше языков. Разбираем счётчики, суммы, максимумы и их ловушки.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
json · Урок 15
Фильтрация ответа API: цены, рейтинги, флаги
Товары из ответа API приехали — теперь вопросы: что дешевле N, что в наличии, какая средняя цена и сколько процентов подходит под условие.
python обработка данных из apiобработка ответа api python
NumPy · Урок 2
Создание массивов в NumPy: arange, linspace, zeros, ones и другие способы
Все конструкторы NumPy на одной странице: arange против linspace, заготовки zeros и ones, матрица eye — и шпаргалка, когда какой способ брать.
как создать массив numpy из спискаnumpy функции с примерами
Функции · Урок 9
sorted с key в Python: сортировка по правилу
Порядок по своему правилу: слова по длине, пары по цене, участники по очкам. Разбираем sorted, параметр key, reverse и стабильность Timsort.
key функция pythonpython функции уроки