目录
- 匿名函数(lambda表达式)
- 三元表达式
高阶函数
- map
- reduce
- filter
- 函数式编程与命令式编程小结
- 装饰器
上一章节的闭包,是函数式编程思维的一种应用。这一章节,将介绍函数式编程的其他应用方。
一、匿名函数(lambda表达式)
顾名思义,没有名字的函数。叫法不太好,没有体现表达式的属性。
严谨点,最好应该叫lambda表达式。
本质:函数
1.定义
| 普通的有名字的函数 | 匿名函数(lambda表达式) | |
|---|---|---|
| 定义方式 | def f():....pass | lambda parameter_list:expression |
| 例题 | def add():....return x + y | lambda x,y:x + y |
| 特点 | 没有了add的函数名 不需要 return 关键字 | |
| 调用方式 | add(1,2)-->3 | f = lambda x,y:x + y....print(f(1,2))-->3 |
上述lambda表达式的调用,只是将其赋值给一个变量f。是无意义的调用,因为这不又跟左边的调用没区别了么,也就是说f = add。
那真正的调用方式,后面会讲。
未来的我:
将lambda表达式,作为参数,传递进map、reduce、filter函数中。它其实是个算子,相当于命令式编程中的def功能,只不过简化为只有参数、内部实现。
2.注意事项
lambda表达式中的第二个位置,只能是表达式,不能是代码块。
f = lambda x,y:x + y #第二个位置,只能是表达式
print(f(1,2))
-->
3f = lambda x,y:a = x + y #第二个位置,改成代码块
print(f(1,2))
-->
error ❌七月老师:
在C#中,匿名函数与lambda表达式,其实是两个不同的东西。
而在python中,指的是一个东西。
二、三元表达式
本质:表达式版本的if else逻辑
功能:用于条件判断
场景:常作为表达式,用在lambda表达式的第二个位置
例题:
有两个数字x,y,如果x>y,就返回x。否则,返回y。用三元表达式编写?
| 其他语言常用的经典写法 | python语言 | |
|---|---|---|
| 对比 | x > y ? x : y | 条件为真时返回的结果 if条件判断 else 条件为假时返回的结果x if x > y else y |
由于只是个表达式,非完整的代码。所以,通常对表达式会有一个执行过程,用变量r来接受其结果。
完整的写法:
x = 2
y = 1
r = x if x > y else y
print(r)
-->
2三、高阶函数
(一)map
之前学习的闭包,是函数式编程的一种应用,推荐在写框架、写类库时使用,不推荐在业务逻辑中使用。
然而,函数式编程思维最有体现的,却是map函数。推荐多多使用。
1.定义
结构:
map(func,*iterables) #*iterables:可重复、可迭代、可遍历的一组数据,序列2.例题:
求列表[1,2,3,4,5,6,7,8]下面每个元素的平方,并以列表的形式呈现出来?
写法1:
用for循环
list_x = [1,2,3,4,5,6,7,8]
list_y = [] #定义一个空列表,作为接受新元素的空葫芦
def square(x):
return x * x
for x in list_x:
y = square(x)
list_y.append(y) #⭐利用的是内置函数appendix,将每次生成的新元素,传入添加到上述空列表中
print(list_y)
-->
[1, 4, 9, 16, 25, 36, 49, 64]写法2:
list_x = [1,2,3,4,5,6,7,8]
def square(x):
return x * x
r = map(square,list_x) #map会把集合中的每个元素,都传入到square中去,并接受square的返回结果
print(r)
-->
<map object at 0x000001C1CC22AE30> #得到的是map对象用内置函数list(),将map对象里面的元素读取出来:
list_x = [1,2,3,4,5,6,7,8]
def square(x):
return x * x
r = map(square,list_x)
print(list(r))
-->
[1, 4, 9, 16, 25, 36, 49, 64]小结:
map函数,到底有什么用?
- 从数学角度:
就是个映射
始:[1,2,3,4,5,6,7,8]
末:[1, 4, 9, 16, 25, 36, 49, 64]
- 从好理解的角度:
就当作执行了一次for循环,for循环内部都调用了square函数。
3.map函数与lambda表达式的结合
这是更能体现map优势的用法
即,用lambda表达式来代替上述写法中的square函数。
(1)写法3:
list_x = [1,2,3,4,5,6,7,8]
r = map(lambda x:x * x,list_x) #只用了两行代码,比for循环简洁、优美、易懂
print(list(r))
-->
[1, 4, 9, 16, 25, 36, 49, 64]
这才是真正的用函数式编程思维来解决问题。
(2)以上简单是因为输入的参数只有一个,那如果是多个参数呢?
list_x = [1,2,3,4,5,6,7,8]
list_y = [1,2,3,4,5,6,7,8]
r = map(lambda x,y:x * x + y,list_x,list_y)
#lambda表达式与map函数的参数位置,都要加上新的参数
print(list(r))
-->
[2, 6, 12, 20, 30, 42, 56, 72](3)如果是两个列表中的元素个数不相等呢?
list_x = [1,2,3,4,5,6,7,8] # 8个元素
list_y = [1,2,3,4,5,6] # 6个元素
r = map(lambda x,y:x * x + y,list_x,list_y)
print(list(r))
-->
[2, 6, 12, 20, 30, 42] # 6个元素。取决于较小长度的列表的长度(二)reduce
1.定义结构
上节map是位于全局命名空间的,所以直接使用即可。
但是,reduce,不是位于全局命名空间,所以,要先从函数库模块functools中导入。
from functools import reduce #导入的是reduce函数
reduce(func,sequence,initial=None)
# func:函数,必须有两个参数
# sequence:序列,即按顺序排列的数据,即字符串str、列表list、元组tuple。
# initial=None:初始值,默认是0可能是一个参数列表,但必须是两个参数。这是基于其自我连续计算的功能设定的。
2.例题:
求列表[1,2,3,4,5,6,7,8]中所有元素的和?
from functools import reduce #不要忘记要先导入,否则报错reduce未定义
list_x = [1,2,3,4,5,6,7,8]
r = reduce(lambda x,y:x + y,list_x) #奇怪:y从哪里来的?
print(r)
-->
36 #得到的是一个数值(1)执行过程
- 初始情况:
不只是拿到1,而是拿到1、2,取前两个元素对应x、y进行相加 - 第二次开始:
将前两个元素的结果3作为x,再次传入lambda表达式中,此时的y是第三个元素 - 以此类推
(2)执行过程演示
(((1+2)+3)+4)+5+...有(1)和(2)可知:
其本质是在做连续计算,在连续调用lambda表达式。
关键点是:上一次的结果,将作为下一次的参数进行运算。直至遍历完。
3.误区
reduce都是做连续相加运算的?
错误。应该是reduce是做连续计算的,不止可以做连续相加,还可以做连续相乘等运算。具体运算方式,取决于lambda表达式是如何定义的。
4.思考题:
在应用场景中,旅行者变成了二维走路,求其坐标?
(x,y)
(0,0)
(1,3) result = (1,3)
(2,-2) reuslt = (3,1)
(-2,3) result = (1,4)
本题的关键点:
每次调用函数时,需要保存上一次函数调用的结果。from functools import reduce
list_x = [(0,0),(1,3),(2,-2),(-2,3)]
r = reduce(lambda x,y:x + y,list_x)
print(r)
-->
(0, 0, 1, 3, 2, -2, -2, 3) ?5.关于第三个参数initial
from functools import reduce
list_x = ['1','2','3','4','5','6','7','8']
r = reduce(lambda x,y:x + y,list_x,'aaa') # 'aaa'是初始值
print(r)
-->
aaa12345678 #所以'aaa'出现在最前面,即第一次运算是:'aaa'+‘1’拓展
谷歌提出了一个大数据计算模型:“map/reduce”.其中,map是映射,reduce是归约
最主要的应用:并行计算
借鉴思想:函数式编程
我的看法:
map就是在做循环计算,reduce就是在做自我连续计算。
共性是:都是在遍历。
区别是:(1)map得到是列表,reduce得到的是值。
(2)reduce上一次的结果,将作为下一次的参数进行运算。
(三)filter
顾名思义,就是将不符合我们规则的元素给过滤掉。
所以,其本质是条件判断。
即,常与lambda表达式、三元表达式配合使用。
所以,filter的特点,必须依托上面两位方能实现。
1.定义结构
filter(func,literable) #filter是个类
#func:结果必须是能够返回可以判断真假的结果。eg True,False;1,0;非空与空
#见第3章 条件控制语句 if mood
#见第1章 bool类型2.例题
把列表中所有数值为0的元素,剔除掉?
list_x = [1,0,1,0,0,1]
r = filter(lambda x:True if x==1 else False,list_x)
print(r)
print(list(r))
-->
<filter object at 0x000001494BADAE30> #跟map一样,得到的是对象
[1, 1, 1]也可以写为
list_x = [1,0,1,0,0,1]
r = filter(lambda x:x,list_x)
#lambda表达式中的算式只用x即可,因为其结果要么是1,要么是0.刚好
print(list(r))
-->
[1, 1, 13.思考题
将列表中的小写的元素都剔除掉?
list_x = [A,a,B,b,C,c]
r= filter(lambda x:x if x in [A-Z] else None,list_x)
print(list(r))
-->
?上述判断大小写,在数据处理方面非常有用。因为通常,我们是通过大写,来判断是否是句子开头。
四、函数式编程与命令式编程小结对比
1.命令式编程的关键点
其实就是我们每天都在写的过程控制。
无论多么复杂的业务逻辑,控制流程的关键点是:
- 要有函数 def
- 要有条件判断 if else
- 要有循环 for
上述三个,是命令是编程最显著的特征。
2.对于函数式编程,常用的关键字有:
- 要有lambda算子(函数式编程最基本的单元)---------代替函数 def
- 要有filter表达式、三元表达式--------------------代替 if else
- 要有map表达式、reduce表达式---------------------代替 for 循环
理论上,函数式编程可以一定程度上,代替命令式编程。但不建议,全盘用函数式编程,构建业务逻辑与项目.
因为
| 命令式语言 | 函数式编程专门语言 |
|---|---|
| Java、C#、Python | lisp |
| 只是对函数式编程有一定程度的支持,但并不是函数式编程的语言 | 人工智能用的比较多 |
| 目前,是大家习惯 |
上述函数式编程的高阶函数,只是让代码更加简洁,不会提升运行效率。


Comments | NOTHING