Работа с циклами почти во всех языках программирования имеет свои нюансы, особенно если в теле цикла выполняется изменение данных по определенным критериям.
В одних языках программирования циклы имеют сложный синтаксис, а в других — наоборот, простой. Python сочетает оба варианта: с одной стороны, он предлагает стандартные ключевые слова for
, while
и break
, а с другой — специальные функции для итерации последовательностей и изменения их данных. Одна из таких функций — map()
.
В этой статье мы подробно разберем, что делает функция map()
в Питоне и для каких задач она подходит больше всего.
Все демонстрируемые скрипты запускались с помощью интерпретатора Python версии 3.10.12, установленном на облачном сервере Timeweb Cloud под управлением операционной системы Ubuntu 22.04.
Каждый скрипт размещался в отдельном файле с расширением .py
(например, some_script.py
), после чего запускался с помощью команды интерпретатора Python:
python some_script.py
cloud
Функция map()
в Python — это встроенная функция, которая применяет указанный метод ко всем элементам одного или нескольких итерируемых объектов, после чего возвращает итератор с результатами этих применений.
В самом простом случае код с map()
выглядит примерно так:
# два списка с числами
firstNumbers = [10, 20, 30]
secondNumbers = [40, 50, 60]
# функцию для сложения чисел из двух списков
def addNumbers(first, second):
return first + second
# создание итератора функцией map()
iterator = map(addNumbers, firstNumbers, secondNumbers)
# итерация новых значений с выводом в консоль
for value in iterator:
print(value)
Без map()
эквивалентная логика записывалась бы следующим образом:
firstNumbers = [10, 20, 30]
secondNumbers = [40, 50, 60]
def addNumbers(first, second):
return first + second
for i in range(len(firstNumbers)):
print(addNumbers(firstNumbers[i], secondNumbers[i]))
Или даже так:
firstNumbers = [10, 20, 30]
secondNumbers = [40, 50, 60]
for i in range(len(firstNumbers)):
print(firstNumbers[i] + secondNumbers[i])
Во всех случаях консольный вывод будет одинаковым:
50
70
90
Код с использованием метода map()
в Python имеет ряд преимуществ по сравнению с кодом, в котором используются более традиционные способы изменения значений итерируемых объектов:
map()
.filter()
, reduce()
, sorted()
, any()
, all()
без промежуточных объектов.Таким образом, map()
— быстрый, оптимизированный (за счет ленивой обработки) и декларативный способ применения простой функции ко всем элементам одной или нескольких последовательностей.
Функция map()
имеет следующую схему:
map(ФУНКЦИЯ, ОБЪЕКТ_1, ОБЪЕКТ_2, ...)
Возвращаемым значением является итерируемый объект <map object>
.
То есть map()
применяет функцию с фиксированным числом аргументов, которое соответствует количеству итерируемых объектов.
В самом простом случае, выполнив функцию map()
, можно получить итерируемый объект, который впоследствии может быть использован самыми разными способами:
firstNumbers = [10, 20, 30]
secondNumbers = [40, 50, 60]
def addNumbers(first, second):
return first + second
iterator = map(addNumbers, firstNumbers, secondNumbers)
print(iterator) # ВЫВОД: <map object at 0x78231f7f9cf0>
Полученный итератор можно перебирать с помощью функции next()
:
firstNumbers = [10, 20, 30]
secondNumbers = [40, 50, 60]
def addNumbers(first, second):
return first + second
iterator = map(addNumbers, firstNumbers, secondNumbers)
print(next(iterator)) # ВЫВОД: 50
print(next(iterator)) # ВЫВОД: 70
print(next(iterator)) # ВЫВОД: 90
Можно перебрать сразу все значения с помощью цикла for
:
firstNumbers = [10, 20, 30]
secondNumbers = [40, 50, 60]
def addNumbers(first, second):
return first + second
iterator = map(addNumbers, firstNumbers, secondNumbers)
for value in iterator:
print(value)
Или с помощью цикла while
:
firstNumbers = [10, 20, 30]
secondNumbers = [40, 50, 60]
def addNumbers(first, second):
return first + second
iterator = map(addNumbers, firstNumbers, secondNumbers)
while True:
try:
value = next(iterator)
except StopIteration:
break
print(value)
Итератор — не самый удобный объект для работы с последовательностями. Его области применения весьма специфичны. Поэтому итерируемый объект можно сконвертировать в список — так проще работать с элементами:
firstNumbers = [10, 20, 30]
secondNumbers = [40, 50, 60]
def addNumbers(first, second):
return first + second
# создание списка из итератора, возвращаемого функцией map()
result = list(map(addNumbers, firstNumbers, secondNumbers))
print(result) # ВЫВОД: [50, 70, 90]
Если в map()
передать количество списков, не соответствующее количеству аргументов обрабатываемой функции, то появится ошибка:
justNumbers = [10, 20, 30]
def addNumbers(first, second):
return first + second
result = list(map(addNumbers, justNumbers)) # ОШИБКА
Таким образом, количество переданных списков должно равняться количеству аргументов обрабатываемой функции:
justNumbers = [10, 20, 30]
def addNumbers(justArgument):
return justArgument + 10
result = list(map(addNumbers, justNumbers))
print(result) # ВЫВОД: [20, 30, 40]
При этом никто не запрещает использовать большее количество списков:
firstNumbers = [10, 20, 30]
secondNumbers = [40, 50, 60]
thirdNumbers = [80, 90, 100]
fourthNumbers = [110, 120, 130]
justStrings = ['a', 'b', 'c']
def addAll(first, second, third, fourth, something):
return str(first) + something + str(second) + something + str(third) + something + str(fourth)
iterator = map(addAll, firstNumbers, secondNumbers, thirdNumbers, fourthNumbers, justStrings)
for value in iterator:
print(value)
Консольный вывод этого примера будет иметь следующий вид:
10a40a80a110
20b50b90b120
30c60c100c130
Преобразование элементов в map()
можно выполнять с помощью лямбда-функций — небольших анонимных функций, определяемых «на лету» без ключевого слова def
:
someNumbers = [1, 2, 3, 4]
result = map(lambda number: number ** 2, someNumbers)
print(list(result)) # ВЫВОД: [1, 4, 9, 16]
Как и обычная функция, лямбда-функция может иметь несколько аргументов:
someNumbers = [1, 2, 3, 4]
someNumbersExponents = [4, 3, 2, 1]
result = map(lambda number, exponent: number ** exponent, someNumbers, someNumbersExponents)
print(list(result)) # ВЫВОД: [1, 8, 9, 4]
Как правило, лямбда-выражения используются в тех местах, где нужна простая логика без повторного использования. Например, в map()
, filter()
, sorted()
, min()
и множестве других функций, предназначенных для преобразования данных.
На самом деле функцию map()
можно использовать для создания любых итерируемых объектов, которые поддерживает язык Python:
# list
l = list(map(lambda x: x * 2, [1, 2, 3, 4]))
print(l) # ВЫВОД: [2, 4, 6, 8]
# tuple
tup = tuple(map(str, [1,2,3]))
print(tup) # ВЫВОД: ('1', '2', '3')
# set
s = set(map(int, ["1","2","1","3"]))
print(s) # ВЫВОД: {1, 2, 3}
# frozenset
fs = frozenset(map(lambda x: x % 2, [1,2,3,4]))
print(fs) # ВЫВОД: frozenset({0, 1})
# dict
d = dict(map(lambda x: (x, x*x), [1,2,3]))
print(d) # ВЫВОД: {1: 1, 2: 4, 3: 9}
# bytes
b = bytes(map(lambda x: x+100, [0,1,2]))
print(b) # ВЫВОД: b'def'
# bytearray
ba = bytearray(map(int, [65,66,67]))
print(ba) # ВЫВОД: bytearray(b'ABC')
Это лишь основные объекты, которые могут быть созданы с помощью map()
. Более того, любая функция или класс, принимающие на вход объект Iterable
или Iterator
, может сразу работать с объектом map
без предварительного преобразования.
Метод map()
может использоваться со множеством встроенных функций языка Python — для наглядности их удобнее представить в виде отдельных категорий:
bytearray
, bytes
, chr
, ord
, str
, repr
, int
, float
, complex
, bool
, dict
, list
, tuple
, set
, frozenset
, memoryview
.abs
, divmod
, pow
, round
, max
, min
, sum
, hex
, oct
, bin
, complex
.len
, iter
, next
, enumerate
, filter
, map
, zip
, sorted
, reversed
, slice
, range
.classmethod
, staticmethod
, property
, getattr
, setattr
, hasattr
, delattr
, isinstance
, issubclass
, type
, super
, object
.dir
, globals
, locals
, vars
, help
, id
, callable
, hash
.all
, any
.print
, input
, open
, eval
, exec
, compile
, import
.format
, ascii
.Встроенных функций, способных работать в связке с map()
, так много, что в этом руководстве будут рассмотрены лишь самые основные.
В самом простом случае функцию map()
можно использовать для преобразования элементов итерируемых объектов в другие типы:
# конвертация строк в целые числа с помощью int()
someStrings = ['20', '30', '40', '50']
result = map(int, someStrings)
print(list(result)) # ВЫВОД: [20, 30, 40, 50]
# конвертация строк в вещественные числа с помощью float()
someStrings = ['20', '30.5', '40', '50']
result = map(float, someStrings)
print(list(result)) # ВЫВОД: [20.0, 30.5, 40.0, 50.0]
# конвертация строк в булевы с помощью bool()
someStrings = ['', 'hello', '', 'world']
result = map(bool, someStrings)
print(list(result)) # ВЫВОД: [False, True, False, True]
# конвертация строк в кортежи с помощью tuple()
someStrings = ['abc', 'hello', 'xyz']
result = map(tuple, someStrings)
print(list(result)) # ВЫВОД: [('a', 'b', 'c'), ('h', 'e', 'l', 'l', 'o'), ('x', 'y', 'z')]
# конвертация строк в сеты с помощью set()
someStrings = ['abc', 'aabb', 'abca']
result = map(set, someStrings)
print(list(result)) # ВЫВОД: [{'a', 'b', 'c'}, {'a', 'b'}, {'a', 'b', 'c'}]
# конвертация чисел в строки с помощью str()
someNumbers = [20, 30.5, 40, 50]
result = map(str, someNumbers)
print(list(result)) # ВЫВОД: ['20', '30.5', '40', '50']
# конвертация любых элементов в строку с помощью str()
someElements = [10, 3.14, True, None, {"a": 1}]
result = map(str, someElements)
print(list(result)) # ВЫВОД: ['10', '3.14', 'True', 'None', "{'a': 1}"]
# преобразование строк в верхний регистр с помощью str.upper()
someStrings = ['just', 'a', 'map', 'tutorial']
result = map(str.upper, someStrings)
print(list(result)) # ВЫВОД: ['JUST', 'A', 'MAP', 'TUTORIAL']
# преобразование строк в нижний регистр с помощью str.lower()
someStrings = ['JUST', 'A', 'MAP', 'TUTORIAL']
result = map(str.lower, someStrings)
print(list(result)) # ВЫВОД: ['just', 'a', 'map', 'tutorial']
Функции, выполняющие преобразования отдельных чисел, могут быть использованы в связке с map()
:
# получение абсолютных значений с помощью abs()
someNumbers = [-20, -30, 40, -50]
result = map(abs, someNumbers)
print(list(result)) # 20, 30, 40, 50]
# округление чисел с помощью round()
someFloats = [13.47, 6.19, 29.743, 5.3724]
result = map(round, someFloats)
print(list(result)) # ВЫВОД: [13, 6, 30, 5]
# округление чисел до заданного числа знаков с помощью round()
someFloats = [13.47, 6.19, 29.743, 5.3724]
someFloatsDigits = [5, 1, 2, 1]
result = map(round, someFloats, someFloatsDigits)
print(list(result)) # ВЫВОД: [13.47, 6.2, 29.74, 5.4]
# возведение чисел в степень с помощью pow()
someNumbers = [1, 2, 3, 4]
someNumbersExponents = [4, 3, 2, 1]
result = map(pow, someNumbers, someNumbersExponents)
print(list(result)) # ВЫВОД: [1, 8, 9, 4]
С помощью map()
можно выполнять операции над последовательностью последовательностей:
# определение длины строк с помощью len()
someStrings = ['just', 'a', 'map', 'tutorial']
result = map(len, someStrings)
print(list(result)) # ВЫВОД: [4, 1, 3, 8]
# инвертирование порядка символов с помощью reverser()
someStrings = ['abc', 'hello', 'xyz']
result = map(reversed, someStrings)
print([list(r) for r in result]) # ВЫВОД: [['c', 'b', 'a'], ['o', 'l', 'l', 'e', 'h'], ['z', 'y', 'x']]
Многие функции, работающие с экземплярами классов, могут быть использованы в map()
:
# определение типа с помощью type()
someItems = [1, 'hello', 3.14, [1, 2, 3], {'a': 1}]
result = map(type, someItems)
print(list(result)) # ВЫВОД: [<class 'int'>, <class 'str'>, <class 'float'>, <class 'list'>, <class 'dict'>]
# определение принадлежности к классу с помощью isinstance()
someItems = [1, 'hello', 3.14, [1, 2], {'a': 1}]
expectedTypes = [int, str, float, int, dict]
result = map(isinstance, someItems, expectedTypes)
print(list(result)) # ВЫВОД: [True, True, True, False, True]
Вспомогательные функции, предоставляющие дополнительную информацию об объектах Python, тоже могут быть использованы вместе с map()
:
# генерация хеша с помощью hash()
someItems = ['abc', 123, (1, 2, 3), frozenset([4, 5]), True]
result = map(hash, someItems)
print(list(result)) # ВЫВОД: [7400903789440981107, 123, 529344067295497451, 8240589813894422664, 1]
# определение вызываемости с помощью callable()
someItems = [len, 42, "hello", lambda x: x * 2, print]
result = map(callable, someItems)
print(list(result)) # ВЫВОД: [True, False, False, True, True]
Функция map()
позволяет формировать логические цепочки:
# создание логического вывода помощью функции all()
someLists = [
[True, True, True],
[True, False, True],
[False, False, False]
]
all_results = map(all, someLists)
print(list(all_results)) # ВЫВОД: [True, False, False]
# создание логического вывода помощью функции any()
any_results = map(any, someLists)
print(list(any_results)) # ВЫВОД: [True, True, False]
С помощью функции map()
можно выполнить множественный вывод в консоль или исполнить сразу несколько выражений:
# выполнение выражений с помощью функции eval()
expressions = ['1+2', '3*4', '10/2', '2**3']
result = map(eval, expressions)
print(list(result)) # ВЫВОД: [3, 12, 5.0, 8]
# вывод в консоль с помощью функции print()
items = ['apple', 'banana', 'cherry']
list(map(print, items)) # ВЫВОД:
# apple
# banana
# cherry
С помощью map()
можно применить функции преобразования значений сразу ко всем элементам итерируемого объекта:
# форматирование значений с помощью функции format()
numbers = [10, 255, 1024]
result = map(format, numbers)
print(list(result)) # ВЫВОД: ['10', '255', '1024']
# применение кодировки с помощью функции ascii()
texts = ['café', 'niño', 'こんにちは']
result = map(ascii, texts)
print(list(result)) # ВЫВОД: ["'caf\\xe9'", "'ni\\xf1o'", "'\\u3053\\u3093\\u306b\\u3061\\u306f'"]
В функцию map()
можно передавать методы класса с помощью ключевого слова self
:
class Equipment():
def __init__(self):
self.power = 10
def recalculate(self, someNumber):
return someNumber * self.power
def use(self, things):
return list(map(self.recalculate, things))
someNumbers = [10, 30, 50, 120]
someEquipment = Equipment()
result = someEquipment.use(someNumbers)
print(result) # ВЫВОД: [100, 300, 500, 1200]
Для полного понимания, какие задачи может решать функция map()
, ее использование лучше всего рассмотреть на примере, приближенном к реальности.
Предположим, есть набор физически существующих датчиков температуры, которые собирают информацию об окружающей среде и передают ее на удаленный сервер для обработки сырых данных. Этот сервер подготавливает наглядные отчёты в понятном человеку виде — чем больше метрик, тем лучше.
Одна из важных функций подобного обработчика — автоматическая конвертация температуры из разных систем счисления. Например, из градусов Цельсия в градусы Фаренгейта и наоборот.
В этом случае функция map()
может быть использована для последовательного однообразного преобразования данных, собранных большим количеством датчиков:
import random
from datetime import datetime
# функция конвертации Цельсия в Фаренгейты
def celsiusToFahrenheit(temperature):
return (temperature * 9/5) + 32
# список сенсоров и список сообщений от них
sensors = ['A1', 'B2', 'C3', 'D4', 'E5', 'F6', 'G7', 'H8', 'I9', 'J10']
data = []
# имитация получения данных от датчиков температуры
for name in sensors:
data.append({'name': name, 'temperature': round(random.uniform(-10, 35), 1)}) # имитация вариативности показателей температуры от −10 до +35 °C
# подготовка отчета с полными данными о температуре окружающей с помощью функции main()
dataNew = map(
lambda sensorData: {
'name': sensorData['name'],
'temperatureC': sensorData['temperature'],
'temperatureF': round(celsiusToFahrenheit(sensorData['temperature']), 1)
},
data
)
# сортировка итоговых результатов и конвертация их в список по убыванию градусов Фаренгейта
dataReport = sorted(dataNew, key=lambda x: x['temperatureF'], reverse=True)
# вывод отчета в консольный терминал
print("-" * 30)
print(f"Отчет — {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("-" * 30)
print(f"{'Датчик':<8}{'°C':>6}{'°F':>8}")
print("-" * 30)
for rec in dataReport:
print(f"{rec['name']:<8}{rec['temperatureC']:>6.1f}{rec['temperatureF']:>8.1f}")
print("-" * 30)
List comprehension в Python — это компактный способ создать новый список, применив выражение к каждому элементу итерируемого объекта.
Конструкция имеет следующую схему:
ВЫРАЖЕНИЕ for ЭЛЕМЕНТ in ОБЪЕКТ
В данном случае:
Например, можно возвести каждый элемент уже существующего списка в степень, получив совершенно новый список:
numbers = [1, 2, 3, 4, 5]
squares = [x**2 for x in numbers]
print(squares) # ВЫВОД: [1, 4, 9, 16, 25]
Функция map()
и конструкция list comprehension решают похожие задачи — применяют функцию к элементам существующего итерируемого объекта, получая новый итерируемый объект.
Однако между ними есть ряд существенных различий:
map()
в Питоне возвращает ленивый итератор который выдает (выполняет функцию) значения по мере обхода, а list comprehension сразу создает список со всеми результатами.map()
не вычисляет значения всех элементов сразу, а выполняет расчет по мере надобности. Напротив, list comprehension сразу вычисляет конечный результат, целиком сохраняя все новые значения.map()
работает быстрее list comprehension — она реализована на C без генерации байт-кода Python в каждом шаге.map()
подчеркивает функциональный стиль, а list comprehension ближе к классическому стилю Python, который зачастую читается естественнее.map()
поддерживает параллельную обработку сразу несколько итерируемых объектов, а list comprehension потребует для этого использование дополнительной функции zip()
.map()
требует готовой функции или лямбда, а list comprehension — только выражение Python.Функцию map()
лучше всего использовать, когда заранее есть функция (встроенная или пользовательская), которую необходимо применить ко всем элементам по мере надобности.
Конструкция list comprehension предпочтительна, когда необходимо сразу получить список новых элементов с использованием условий, вложенных циклов или другой сложной логики.
Подготовили для вас выгодные тарифы на облачные серверы
Функция map()
— универсальный инструмент для параллельного изменения значений элементов одного или нескольких итерируемых объектов.
Работает быстро, оптимизирована для большого объема данных и имеет функциональный, декларативный стиль, позволяющий описывать цепочки преобразований в связке со множеством других похожих функций.
Короче говоря, это «однолинейный конвейер» для превращения данных: на вход подаются функция и последовательности, а на выходе получается «ленивый» результат. Просто и быстро!