Функции высшего порядка: map, filter, reduce, partial
Функции высшего порядка
Функция высшего порядка принимает функцию как аргумент или возвращает функцию. Вы уже встречали их: sorted(key=...), max(key=...), фабрики-замыкания.
def apply(func, values):
return [func(v) for v in values]
print(apply(abs, [-1, 2, -3]))
print(apply(str.upper, ["a", "b"]))
print(apply(lambda x: x ** 3, [1, 2, 3]))
map
map(func, iterable, ...) применяет функцию к каждому элементу. Возвращает ленивый итератор:
nums = ["1", "2", "3"]
m = map(int, nums)
print(m) # ещё ничего не вычислено
print(list(m)) # вычисление при переборе
print(list(m)) # итератор одноразовый
print(list(map(pow, [2, 3, 4], [10, 2, 1]))) # несколько итерируемых — несколько аргументов
print(sum(map(int, "12345"))) # сумма цифр числа
filter
filter(func, iterable) оставляет элементы, для которых функция истинна. С None вместо функции — убирает ложные значения:
nums = [0, 5, -3, 8, 0, 2]
print(list(filter(lambda x: x > 0, nums)))
print(list(filter(None, ["a", "", None, "b", 0])))
print(list(filter(str.isdigit, ["12", "ab", "3"])))
map/filter или включение?
nums = range(10)
a = list(map(lambda x: x * 2, filter(lambda x: x % 3 == 0, nums)))
b = [x * 2 for x in nums if x % 3 == 0]
print(a == b, b)
В Python чаще пишут включения — они читаются легче. map удобен, когда функция уже есть: map(int, items), map(str.strip, lines).
reduce
functools.reduce(func, iterable, initial) сворачивает последовательность в одно значение, применяя функцию попарно слева направо:
from functools import reduce
print(reduce(lambda acc, x: acc + x, [1, 2, 3, 4])) # ((1+2)+3)+4
print(reduce(lambda acc, x: acc * x, [1, 2, 3, 4], 1))
print(reduce(max, [3, 9, 2]))
print(reduce(lambda acc, w: acc + len(w), ["кот", "собака"], 0))
Для частых случаев есть встроенные функции — используйте их: sum, max, min, math.prod, "".join, any, all.
any и all
ages = [25, 17, 34]
print(any(a < 18 for a in ages)) # есть хотя бы один несовершеннолетний
print(all(a >= 18 for a in ages)) # все взрослые
print(any([]), all([])) # False True — для пустых
Обе функции ленивые: any останавливается на первом истинном, all — на первом ложном.
partial
functools.partial фиксирует часть аргументов функции:
from functools import partial
def power(base, exp):
return base ** exp
square = partial(power, exp=2)
two_to = partial(power, 2)
print(square(7), two_to(10))
log_error = partial(print, "[ERROR]", sep=" | ")
log_error("диск заполнен", "код 28")
Модуль operator
Готовые функции для операторов — короче lambda и чуть быстрее:
import operator
from functools import reduce
print(reduce(operator.mul, [1, 2, 3, 4]))
print(list(map(operator.add, [1, 2], [10, 20])))
get_name = operator.itemgetter("name")
print(get_name({"name": "Анна", "age": 30}))
print(operator.attrgetter("real")(3 + 4j), operator.methodcaller("upper")("abc"))
Композиция функций
def compose(f, g):
return lambda x: f(g(x))
strip_upper = compose(str.upper, str.strip)
print(strip_upper(" привет "))
pipeline = [str.strip, str.lower, lambda s: s.replace(" ", "-")]
text = " Заголовок Статьи "
for step in pipeline:
text = step(text)
print(text)
Итог
- Функции — объекты первого класса: их передают и возвращают.
mapиfilterвозвращают ленивые одноразовые итераторы; в коде чаще используют включения.reduceсворачивает последовательность; для типичных случаев естьsum,max,math.prod.any/all— ленивые проверки;partialфиксирует аргументы;operator— функции-операторы.
Практика решено 0 из 5
С помощью map и filter (без списковых включений) получите список result:
квадраты нечётных чисел из nums.
Какие тесты проверяют код (1)
assert result == [1, 9, 25, 49]
nums = [1, 2, 3, 4, 5, 6, 7]
result = list(map(lambda x: x * x, filter(lambda x: x % 2, nums)))
С помощью functools.reduce напишите функцию product(nums) — произведение всех чисел списка.
Для пустого списка верните 1.
Какие тесты проверяют код (3)
assert product([1, 2, 3, 4]) == 24 assert product([]) == 1 assert product([5]) == 5
from functools import reduce
def product(nums):
return reduce(lambda acc, x: acc * x, nums, 1)
Напишите функцию compose(*funcs), которая возвращает композицию функций, применяемых
справа налево: compose(f, g, h)(x) == f(g(h(x))). Без функций — тождественная функция.
Какие тесты проверяют код (3)
inc = lambda x: x + 1; dbl = lambda x: x * 2; assert compose(inc, dbl)(5) == 11 assert compose(str, abs)(-3) == "3" assert compose()(42) == 42
def compose(*funcs):
def composed(x):
for f in reversed(funcs):
x = f(x)
return x
return composed
Что выведет код?
m = map(str.upper, ["a", "b"])
print(list(m))
print(list(m))
С помощью functools.partial создайте функцию to_int_base2, которая переводит строку из двоичной
системы в число (на основе встроенной int(x, base)).
Какие тесты проверяют код (2)
assert to_int_base2("1010") == 10
assert to_int_base2("11111111") == 255
from functools import partial
to_int_base2 = partial(int, base=2)
Войдите, чтобы отмечать пройденные уроки.