Social Profiles

My Projects

Функции map и zip и lambda. Python

Функции map, zip и лямбда (кстати говоря называются "функции высшего порядка" или "first-class-functions") позволяют достаточно просто выполнять различные манипуляции с данными, для чего в "обычном" процедурном стиле приходится писать немного больше кода. Все ниженаписанное относится к так называемому функциональному программированию, луркайте подробности.

Функции map, zip и lambda в примерах.

Простая задача есть список a = [1, 2] и список b = [3, 4] одинаковой длины и нужно слить их парами. Проще простого - используя функцию zip :

a = [1,2]
b = [3,4]
print zip(a,b)
[(1, 3), (2, 4)]

или тройками :

a = [1,2]
b = [3,4]
c = [5,6]
print zip(a,b,c)
[(1, 3, 5), (2, 4, 6)]

или в более общем виде

list = [a, b, c]
print zip(*list)
[(1, 3, 5), (2, 4, 6)]

Звездочка * перед list как-бы говорит что передается список аргументов, т.е. Действовать эквивалентно тому как если бы передали a, b, c т.е. Можно даже так print zip(*[a, b, c]) результат не изменится.

Далее, функция – map. Случаются ситуации, когда внезапно нужно применить какую-либо функцию к каждому элементу списка. Нуб напишет так :

def f(x):
    return x*x

nums = [1, 2, 3]
for num in nums:
    print f(num)

Более опытный нуб изучивший list comprehensions :

def f(x):
    return x*x
print [f(num) for num in nums]

Программист сделает проще :

def f(x):
    return x*x
print map(f, nums)

А тру-мэдскиллз хакер сделает следующим образом (при условии конечно, что функцию можно записать лямбдой, далеко не всегда функция будет достаточно простой чтобы записать ее лямбдой) :

print map(lambda x: x*x, nums)

Последняя запись являет собой пример наиболее грамотного подхода. Дело в том, что когда человек пишет код как стихи, в порыве вдохновения (что другими словами можно назвать "в диком угаре"), крайне роляет скорость написания (отсюда растут корни трепетной любви многих девелоперов к простым текстовым редакторм vim, emacs, sublimetext), а сильная сторона питона как раз в размере генерируемого кода - он очень компактный. Написать одну строчку естественно быстрее чем 7, да и читать короткий код проще, однако написание подобного кода требует определенного навыка. Другая сторона медали – иногда в этом "диком угаре" пишут в одну строчку целые последовательности достаточно сложных действий, да так что очень трудно понять что там происходит и что получается в конечном итоге.

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

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

a = [1,3,4]
b = [3,4,5]
print map(f, a, b)
[3, 12, 20]

Классно, правда?

Однако если списки разной длины, т.е. Один короче другого, то он будет дополнен значениями None до нужной длины. Если убрать из списка b последнее значение – пример не будет работать, т.к. В функции f произойдет попытка умножения числа на None, и питоне не позволяет это делать, что кстати выгодно отличает его от php, который в подобной ситуации работал бы дальше. Поэтому если функция f достаточно объемна, неплохо бы проверять передаваемые значения. Например ;

def f(x, y):
    if (y == None):
        y = 1    
    return x*y

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

a = [1,3,4]
b = [3,4]
print map(None, a, b)
[(1, 3), (3, 4), (4, None)]

Теперь про лямбда функции в python. Они используются когда вам необходимо определить функцию без исподьзования def func_name(): ..., ведь часто (как в предыдущих примерах) функция настолько мала, что определять её отдельно смыла нет (лишние строчки кода, что ухудшение читабельность). Поэтому функцию можно определить “на месте” f = lambda x: x*x как бы говорит нам – принимает x, возвращает x*x

Так используя стандартные инструменты питона можно записать довольно сложные действия в одну строчку. К примеру функцию :

def f(x, y):
    if (y == None):
        y = 1    
    return x*y

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

lambda x, y: x * (y if y is not None else 1)

А теперь хорошо бы передавать списки отсортированные по длине – len(a) > (b) – проще простого - воспользуемся функцией sorted :

sorted([a, b], key=lambda x: len(x), reverse=True)

фунция sorted принимает список значений ([a,b] = [[1,2],[2,4,5]]) и сортирует по ключу key – который у нас задан функцией len(x) - возвращающей длину списка, сортируем в порядке убывания (reverse=True)

В конечном итоге вся операция записывается таким образом :

map(lambda x, y: x * (y if y is not None else 1), *sorted([a, b], key=lambda x: len(x), reverse=True))

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

comments powered by Disqus