[Python高效编程] - lambda, map, filter, reduce函数

如果编程语言Python的作者Guido van Rossum已经有了自己的意愿,那么在本教程中将会缺少这一章。他在2005年5月发表的文章 "All Things Pythonic: The fate of reduce() in Python 3000"中,他给出了去掉lambda,map(),filter() 和 reduce() 函数的原因。他预计Lisp和计划“伙计”的阻力。他没有预料到的是这种对立的僵化。足以说Guido van Rossum在一年之后写了几句话:“经过这么多的尝试,想出了一个lambda的替代品,也许我们应该承认失败,我没有时间去跟随最近几轮,但我建议我们保持lambda,以停止浪费每个人的天赋和时间在一个不可能的任务。“ 我们可以看到结果:lambda,map() 和 filter() 仍然是Python核心的一部分。只有reduce() 不得不分离,它进入了模块functools。

他的推理是这样的:

  1. lambda,filter,map 和 reduce 也有同样强大的选择,即列表解析
  2. 列表解析更明显,更容易理解
  3. 同时拥有列表解析和“filter,map,reduce和lambda”是超越Python的座右铭“应该有一个明显的方法来解决问题”

lambda 操作

有些人喜欢它,有些人讨厌它,许多人都害怕lambda操作。 lambda运算符或lambda函数是创建小型匿名函数(即没有名称的函数)的一种方法。这些功能是丢弃功能,即它们只是在创建它们时才需要。 Lambda函数主要与函数 filter(),map() 和 reduce() 结合使用。由于Lisp程序员的需求,lambda功能被添加到Python中。

lambda函数的一般语法非常简单:

>>> sum = lambda x, y : x + y
>>> sum(3,4)
7
>>> 

上面的例子可能看起来像一个数学家的玩物。形式主义把一个容易理解的问题变成一个抽象,更难以把握形式主义。最重要的是,我们可以通过使用下面的常规函数​​定义来获得相同的效果:

>>> def sum(x,y):
...     return x + y
... 
>>> sum(3,4)
7
>>>

我们可以向你保证,当你学会使用map()函数的时候,这种方法的好处将会很明显。

map() 函数

正如我们前面提到的,lambda运算符的优点可以在与 map() 函数结合使用时看到。 map() 是一个带有两个参数的函数:

r = map(func, seq)

第一个参数 func 是一个函数的名字,第二个是序列(例如一个列表)seq。 map() 将函数 func 应用于序列 seq 的所有元素。在Python3之前,map() 用于返回一个列表,其中结果列表的每个元素都是 func 应用在列表或元组“seq”的相应元素上的函数的结果。在Python 3中,map() 返回一个迭代器。

下面的例子说明了 map() 的工作方式:

>>> def fahrenheit(T):
...     return ((float(9)/5)*T + 32)
... 
>>> def celsius(T):
...     return (float(5)/9)*(T-32)
... 
>>> temperatures = (36.5, 37, 37.5, 38, 39)
>>> F = map(fahrenheit, temperatures)
>>> C = map(celsius, F)
>>>
>>> temperatures_in_Fahrenheit = list(map(fahrenheit, temperatures))
>>> temperatures_in_Celsius = list(map(celsius, temperatures_in_Fahrenheit))
>>> print(temperatures_in_Fahrenheit)
[97.7, 98.60000000000001, 99.5, 100.4, 102.2]
>>> print(temperatures_in_Celsius)
[36.5, 37.00000000000001, 37.5, 38.00000000000001, 39.0]
>>> 

在上面的例子中,我们没有使用 lambda。通过使用 lambda,我们不必定义和命名函数 fahrenheit() 和 celsius() 。您可以在下面的交互式会话中看到:

>>> C = [39.2, 36.5, 37.3, 38, 37.8] 
>>> F = list(map(lambda x: (float(9)/5)*x + 32, C))
>>> print(F)
[102.56, 97.7, 99.14, 100.4, 100.03999999999999]
>>> C = list(map(lambda x: (float(5)/9)*(x-32), F))
>>> print(C)
[39.2, 36.5, 37.300000000000004, 38.00000000000001, 37.8]
>>> 

map() 可以应用于多个列表。列表必须具有相同的长度。 map() 将其 lambda 函数应用于参数列表的元素,即它首先应用于具有第零索引的元素,然后应用于具有第一索引的元素,直到达到第n个索引:

>>> a = [1,2,3,4]
>>> b = [17,12,11,10]
>>> c = [-1,-4,5,9]
>>> list(map(lambda x,y:x+y, a,b))
[18, 14, 14, 14]
>>> list(map(lambda x,y,z:x+y+z, a,b,c))
[17, 10, 19, 23]
>>> list(map(lambda x,y,z : 2.5*x + 2*y - z, a,b,c))
[37.5, 33.0, 24.5, 21.0]
>>> 

我们可以在上面的例子中看到,参数x从列表a中获得它的值,而y从列表c中的b和z中获得它的值。

mapping 函数列表

上一节的 map 函数被用来将一个函数应用于一个或多个迭代。我们现在将编写一个函数,将一堆函数应用于一个Python对象,这些函数可能是一个迭代项,例如列表或元组。

from math import sin, cos, tan, pi

def map_functions(x, functions):
     """ map an iterable of functions on the the object x """
     res = []
     for func in functions:
         res.append(func(x))
     return res

family_of_functions = (sin, cos, tan)
print(map_functions(pi, family_of_functions))

程序输出:

[1.2246467991473532e-16, -1.0, -1.2246467991473532e-16]

先前定义的 map_functions 函数可以用列表理解技术简化,我们将在章节列表理解中加以说明:

def map_functions(x, functions):
     return [ func(x) for func in functions ]

filter() 函数

函数:

filter(function, sequence) 

提供了一个优雅的方法来过滤出序列“序列”的所有元素,为此函数函数返回True。即如果item包含在序列“sequence”中且如果 function(item) 返回 True,则将由 filter(function,sequence) 的迭代器结果产生项目。 换句话说:函数 filter(f, l) 需要函数 f 作为第一个参数。 f 必须返回一个布尔值,即 True 或 False。这个函数将应用于列表 l 的每个元素。只有当 f 返回 True 时,元素才会被迭代器生成,这是 filter(function,sequence) 的返回值。

在下面的例子中,我们先滤出前11个斐波那契数列的奇数和偶数元素:

>>> fibonacci = [0,1,1,2,3,5,8,13,21,34,55]
>>> odd_numbers = list(filter(lambda x: x % 2, fibonacci))
>>> print(odd_numbers)
[1, 1, 3, 5, 13, 21, 55]
>>> even_numbers = list(filter(lambda x: x % 2 == 0, fibonacci))
>>> print(even_numbers)
[0, 2, 8, 34]
>>> 
>>> 
>>> # or alternatively:
... 
>>> even_numbers = list(filter(lambda x: x % 2 -1, fibonacci))
>>> print(even_numbers)
[0, 2, 8, 34]
>>> 

reduce()函数

正如我们在本教程的这一节的介绍中所提到的。 reduce() 已经从Python的核心中移植到 Python 3 中。Guido van Rossum 讨厌 reduce(),正如我们从2005年3月10日在artima.com发表的声明中可以学到的:

“所以现在 reduce() 这实际上是我一直最讨厌的一个,因为除了几个涉及 + 或 *的例子之外,几乎每次我看到带有非平凡函数参数的 reduce() 在我明白了 reduce() 应该做什么之前,需要用笔和纸来描绘实际上被输入到这个函数的内容,所以在我看来,reduce() 的适用性几乎局限于关联​​运算符,并且总的来说其他情况下,最好明确地写出累积循环。“

函数:

reduce(func, seq) 

不断地将函数 func() 应用到序列 seq 。它返回一个单一的值。
如果 seq = [s1,s2,s3,…,sn],则调用 reduce(func, seq) 如下所示:

  • 首先,seq 的前两个元素将被应用于 func,例如 func(s1, s2) 。reduce() 作用的列表现在如下所示:[func(s1,s2),s3,…,sn]
  • 在下一步中,func 将被应用于前一个结果和列表的第三个元素,即func(func(s1,s2),s3) 列表看起来像这样:[func(func(s1,s2),s3),…,sn]
  • 继续这样做,直到剩下一个元素,并返回这个元素作为reduce()的结果。

如果n等于4,则可以这样解释前面的解释:
这里写图片描述

我们想用一个简单的例子来说明 reduce() 的这种工作方式。我们必须导入functools 才能使用 reduce:

>>> import functools
>>> functools.reduce(lambda x,y: x+y, [47,11,42,13])
113
>>> 

下图显示了计算的中间步骤:
这里写图片描述

reduce()举例
通过使用 reduce 来确定数值列表的最大值:

>>> from functools import reduce
>>> f = lambda a,b: a if (a > b) else b
>>> reduce(f, [47,11,42,102,13])
102
>>> 

计算从1到100的数字总和:

>>> from functools import reduce
>>> reduce(lambda x, y: x+y, range(1,101))
5050

改变前面的例子来计算产品(阶乘)从1到一个数字是非常简单的,但是不要选择100.我们只需要将“+”运算符变成“*”:

>>> reduce(lambda x, y: x*y, range(1,49))
12413915592536072670862289047373375038521486354677760000000000

如果你正在抽签,这里有机会赢得49张图中的6张:

>>> reduce(lambda x, y: x*y, range(44,50))/reduce(lambda x, y: x*y, range(1,7))
13983816.0
>>> 

练习

  1. 想象一下在书店里使用的会计程序。它与子列表一起工作,如下所示:
    这里写图片描述
    编写一个Python程序,它返回一个带有2元组的列表。每个元组由一个订单号和每件商品的价格和数量的乘积组成。如果订单的价值低于100,00欧元,产品应增加10欧元。 用lambda和map编写一个Python程序。
  2. 同一家书店,但这一次我们在另一个名单上工作。我们列表的子列表如下所示: [订货号,(货号,数量,单价),…(货号,数量,单价)] 编写一个返回两个元组列表的程序(订单号,订单总额)。

习题答案

##习题1

orders = [ ["34587", "Learning Python, Mark Lutz", 4, 40.95], 
	       ["98762", "Programming Python, Mark Lutz", 5, 56.80], 
           ["77226", "Head First Python, Paul Barry", 3,32.95],
           ["88112", "Einführung in Python3, Bernd Klein", 	3, 24.99]]

min_order = 100
invoice_totals = list(map(lambda x: x if x[1] >= min_order else (x[0], x[1] + 10), 
			              map(lambda x: (x[0],x[2] * x[3]), orders)))

print(invoice_totals)

前一个程序的输出如下所示:

[('34587', 163.8), ('98762', 284.0), ('77226', 108.85000000000001), ('88112', 84.97)]

##习题2

from functools import reduce

orders = [ [1, ("5464", 4, 9.99), ("8274",18,12.99), ("9744", 9, 44.95)], 
	       [2, ("5464", 9, 9.99), ("9744", 9, 44.95)],
	       [3, ("5464", 9, 9.99), ("88112", 11, 24.99)],
           [4, ("8732", 7, 11.99), ("7733",11,18.99), ("88112", 5, 39.95)] ]

min_order = 100
invoice_totals = list(map(lambda x: [x[0]] + list(map(lambda y: y[1]*y[2], x[1:])), orders))
invoice_totals = list(map(lambda x: [x[0]] + [reduce(lambda a,b: a + b, x[1:])], invoice_totals))
invoice_totals = list(map(lambda x: x if x[1] >= min_order else (x[0], x[1] + 10), invoice_totals))

print (invoice_totals)

我们将得到以下结果:

[[1, 678.3299999999999], [2, 494.46000000000004], [3, 364.79999999999995], [4, 492.57]]

#附录:
原文地址:

https://www.python-course.eu/python3_lambda.php

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值