Функции 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 могут быть разной длины и передаваться в каком угодно порядке. Лямбда-выражения удобны для определения не очень сложных функций, которые передаются затем другим функциям.