Функции map, filter и zip

Функции map, filter и zip

Теперь, когда мы с вами рассмотрели базовые типы данных языка Python, отметим несколько весьма полезных и часто используемых функций. И начнем с функции map. Она позволяет преобразовывать элементы итерируемого объекта в соответствии с некоторой указанной функцией:

map(func, *iterables)

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

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

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

def sq(x):

    return x**2

и вызовем функцию map:

b = map(sq, lst)

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

print(next(b))

print(next(b))

print(next(b))

Или же сформировать новый список с помощью функции list:

a = list(b)

print(a)

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

b = (sq(x) for x in [1,-2,3,-4,-5])

или вот такого списка:

a = [sq(1), sq(-2), sq(3), sq(-4), sq(-5)]

Причем, во всех случаях функция sq или какая-либо другая должна принимать только один аргумент. Если ее записать, например, так:

def sq(x, n):

    return x**n

То возникнет ошибка. А вот возвращать она может несколько аргументов, например, так:

def sq(x):

    return x, x**2

В результате:

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

b = map(sq, lst)

a = list(b)

print(a)

получим список кортежей:

[(1, 1), (-2, 4), (3, 9), (-4, 16), (-5, 25)]

Подобные преобразования можно выполнять с любыми типами данных, например, строками:

lst = ["Москва", "Рязань", "Смоленск", "Тверь", "Томск"]

b = map(len, lst)

a = list(b)

print(a)

На выходе получим список с длинами соответствующих строк:

[6, 6, 8, 5, 5]

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

b = map(str.upper, lst)

И в результате получаем следующий список:

['МОСКВА', 'РЯЗАНЬ', 'СМОЛЕНСК', 'ТВЕРЬ', 'ТОМСК']

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

b = map(lambda x: x[::-1], lst)

Получим строки, записанные наоборот:

['авксоМ', 'ьназяР', 'кснеломС', 'ьревТ', 'ксмоТ']

Почему такая операция так преобразует строки мы с вами уже говорили, когда рассматривали строки и их срезы.

Далее, так как функция map вторым аргументом принимает любой итерируемый объект, то мы можем результат работы первой функции map:

b = map(lambda x: x.replace("а", "А"), lst)

использовать во второй функции map:

c = map(sorted, b)

res1 = list(c)

print(res1)

Получим результат:

[['А', 'М', 'в', 'к', 'о', 'с'], ['А', 'Р', 'з', 'н', 'ь', 'я'], ['С', 'е', 'к', 'л', 'м', 'н', 'о', 'с'], ['Т', 'в', 'е', 'р', 'ь'], ['Т', 'к', 'м', 'о', 'с']]

То есть, строки коллекции b:

['МосквА', 'РязАнь', 'Смоленск', 'Тверь', 'Томск']

были преобразованы в список символов, отсортированных по возрастанию и на выходе мы получили общий список с этими вложенными списками.

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

a = int(input())

и будем вводить целые числа через пробел, то при выполнении функции int возникнет ошибка, т.к. пробел – это не цифровой символ. Хорошо, давайте тогда разобьем эту строку по пробелам:

a = input().split()

print(a)

В результате у нас будет список из строк, в которых записаны введенные числа. Но функцию int можно применять к конкретной строке, а не к списку, т.е. вот такая операция:

print( int(a) )

опять приведет к ошибке. И здесь нам на помощь приходит функция map. Реализуем наш ввод вот таким образом:

a = map(int, input().split())

print( a )

И мы получим генератор для получения введенных чисел. Но это все удобнее преобразовать сразу к списку:

a = list(map(int, input().split()))

И теперь, при вводе любого количества чисел через пробел, будем получать упорядоченный список, состоящий из чисел. Это бывает очень удобно.

Функция filter

Следующая аналогичная функция – это filter. Само ее название говорит, что она возвращает элементы, для которых, переданная ей функция возвращает True:

filter(func, *iterables)

Предположим, у нас есть список

a=[1,2,3,4,5,6,7,8,9,10]

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

def odd(x):

    return x%2

И далее, вызов функции filter:

b = filter(odd, a)

print(b)

На выходе получаем итератор, который можно перебрать так:

print( next(b) )

print( next(b) )

print( next(b) )

print( next(b) )

Или, с помощью цикла:

for x in b:

    print(x, end=" ")

Или же преобразовать итератор в список:

b = list(filter(odd, a))

print(b)

Конечно, в качестве функции здесь можно указывать лямбда-функцию и в нашем случае ее можно записать так:

b = list(filter(lambda x: x%2, a))

И это бывает гораздо удобнее, чем объявлять новую функцию.

Функцию filter можно применять с любыми типами данных, например, строками. Пусть у нас имеется вот такой кортеж:

lst = ("Москва", "Рязань1", "Смоленск", "Тверь2", "Томск")

b = filter(str.isalpha, lst)

 

for x in b:

    print(x, end=" ")

и мы вызываем метод строк isalpha, который возвращает True, если в строке только буквенные символы. В результате в консоли увидим:

Москва Смоленск Тверь Томск

Функция zip

Следующая весьма полезная функция позволяет объединять между собой соответствующие элементы упорядоченных коллекций. Например, у нас имеется два списка:

a = [1,2,3,4]

b = [5,6,7,8]

И вызывая для них функцию zip:

it = zip(a, b)

print(it)

Получим итератор, который возвращает следующую коллекцию:

print( list(it ) )

и мы увидим:

[(1, 5), (2, 6), (3, 7), (4, 8)]

То есть, у нас были объединены в кортеж соответствующие элементы этих двух списков.

Давайте теперь добавим еще один итерируемый объект – строку:

c = "abracadabra"

И вызовем функцию zip для всех этих трех объектов:

it = zip(a, b, c)

print( list(it ) )

В результате получим коллекцию:

[(1, 5, 'a'), (2, 6, 'b'), (3, 7, 'r'), (4, 8, 'a')]

Смотрите, мы здесь имеем всего четыре кортежа, в каждом из которых по три элемента. То есть, все оставшиеся символы строки "abracadabra" были просто отброшены. Получается, что функция zip формирует выходной список, длина которого равна длине наименьшей из указанных коллекций. Если, например, мы уменьшим коллекцию a до двух элементов:

a = [1,2]

то на выходе также получим список из двух элементов:

[(1, 5, 'a'), (2, 6, 'b')]

Вот в этом и заключается удобство этой функции: она позволяет автоматически объединить несколько списков в наборы кортежей из соответствующих значений.

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

Задания для самоподготовки

1. Поставить в соответствие следующим английским символам русские буквы:

h – х, e – е, l – л, o – о, w – в, r – р, d – д

и преобразовать строку «hello world!» в русские символы.

2. Дан текст:

t = """Куда ты скачешь гордый конь,
И где опустишь ты копыта?
О мощный властелин судьбы!
Не так ли ты над самой бездной,
На высоте, уздой железной
Россию поднял на дыбы?"""

Необходимо выделить каждое второе слово из этого стихотворения и представить результат в виде упорядоченного списка. (Подумайте как реализовать алгоритм с наименьшими затратами по памяти).

3. Реализовать алгоритм для нахождения всех делителей натурального числа N. Число N вводится с клавиатуры. Для начала можно реализовать простым перебором всех N возможных чисел (делителей). Затем, подумайте, как можно оптимизировать по скорости этот алгоритм.

icon