参考资料Python教程 - 廖雪峰的官方网站 (liaoxuefeng.com)
四、函数式编程(Functional Programming)
- 高阶函数Higher-order function
变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
map/reduce函数
-
map()
函数-
【输入】两个参数:函数 +
Iterable
-
【输出】
map
将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator
返回 -
举例:把函数f(x)=x2作用在list
[1, 2, 3, 4, 5, 6, 7, 8, 9]
的每个元素上-
>>> def f(x): ... return x * x ... >>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> list(r) [1, 4, 9, 16, 25, 36, 49, 64, 81]
-
-
代码简化 注意:在leetcode中常见这种写法,且处理数据时经常用到map()函数
-
>>> list(map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])) [1, 4, 9, 16, 25, 36, 49, 64, 81]
-
-
-
reduce()
函数-
【输入】:两个参数
-
【作用】:
reduce
把结果继续和序列的下一个元素做累积计算→reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
-
举例:序列求和
-
>>> from functools import reduce >>> def add(x, y): ... return x + y ... >>> reduce(add, [1, 3, 5, 7, 9]) 25
-
-
filter()函数
-
用于过滤序列(别和map()混淆,这里不对原数据进行改变)
-
【输入】一个函数和一个序列
-
【作用】把传入的函数依次作用于每个元素,然后根据返回值是
True
还是False
决定保留还是丢弃该元素。(即:对可迭代对象进行逐一操作,将满足表达式的挑选出来) -
举例
-
def is_odd(n): return n % 2 == 1 list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 结果: [1, 5, 9, 15]
-
sorted()函数
-
排序算法:排序的核心是比较两个元素的大小
- 数字:直接比较
- 其他类型:通过函数抽象出来
-
sorted()
函数-
是一个高阶函数。用
sorted()
排序的关键在于实现一个映射函数。 -
可以直接对list进行排序 sorted([36, 5, -12, 9, -21])
-
可以接收一个
key
函数来实现自定义的排序,例如按绝对值大小排序 sorted([36, 5, -12, 9, -21], key=abs)- key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序
-
默认情况下,对字符串排序,按照ASCII的大小比较
-
由于
'Z' < 'a'
,结果,大写字母Z
会排在小写字母a
的前面。(先排大写,再排小写)-
>>> sorted(['bob', 'about', 'Zoo', 'Credit']) ['Credit', 'Zoo', 'about', 'bob']
-
-
如果想要忽略大小写,需要先转换为相同大/小写
-
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower) ['about', 'bob', 'Credit', 'Zoo']
-
-
如果想要反向排序,让z排前面
-
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True) ['Zoo', 'Credit', 'bob', 'about']
-
-
-
- 返回函数 (需配合实际做题加强理解)
-
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。(返回的不是运算结果)
-
闭包
-
内层函数引用了外层函数的局部变量,即当一个函数返回了一个函数后,其内部的局部变量还被新函数引用
-
❗返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。❗
-
如果一定要引用循环变量怎么办?
-
再创建一个函数,用该函数的参数绑定循环变量当前的值
-
因此无论该循环变量后续如何更改,已绑定到函数参数的值不变
-
def count(): def f(j): def g(): return j*j return g fs = [] for i in range(1, 4): fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f() return fs
-
# 对应结果 >>> f1, f2, f3 = count() >>> f1() 1 >>> f2() 4 >>> f3() 9
-
-
❗ 使用闭包时,对外层变量赋值前,需要先使用nonlocal声明该变量不是当前函数的局部变量。❗
- 如果只是读外层变量的值,我们会发现返回的闭包函数调用一切正常
- 但是,如果对外层变量赋值,会报错
-
tips:返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。
-
- 匿名函数
-
匿名函数
lambda x: x * x
实际上就是:-
def f(x): return x * x
-
-
关键字
lambda
表示匿名函数,冒号前面的x
表示函数参数。 -
限制:只能有一个表达式,不用写
return
,返回值就是该表达式的结果。 -
好处:函数没有名字,不必担心函数名冲突
-
匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数
-
>>> f = lambda x: x * x >>> f <function <lambda> at 0x101c6ef28> >>> f(5) 25
-
-
也可以把匿名函数作为返回值返回
-
def build(x, y): return lambda: x * x + y * y
-
- 装饰器 Decorator (重要,面试官常问)
-
装饰器是一种用于修改函数或方法行为的方式,它们以
@decorator_name
的形式使用。-
(补充:在Python中,
@
符号通常用于装饰器(decorators)和矩阵乘法。) -
装饰器可以用于函数、方法或类。
-
在代码运行期间动态增加功能的方式
-
接受一个函数作为参数,并返回一个函数。
-
# (下面的decorator 是一个装饰器函数,会修改 my_function 的行为。) # 1.没有@时的写法: def my_function(): # 函数体 my_function = decorator(my_function) # 2.简化后写法: @decorator def my_function(): # 函数体
-
用途:装饰器是一种灵活的工具,可用于例如添加日志、权限检查、性能测量等功能。
-
-
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。
-
与第2点返回函数联动,decorator本质上就是一个返回函数的高阶函数。
-
例如,定义一个能打印日志的decorator
-
def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper # wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。
-
- 偏函数 Partial function
-
来自Python的
functools
模块 -
当函数的参数个数太多,需要简化时,使用
functools.partial
可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。 -
使用偏函数后可以直接使用,不许自己专门定义特别用法的固定函数(
functools.partial
可以把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。)-
# 例1:偏函数替代了什么? # 之前:定义函数 def int2(x, base=2): return int(x, base) # 之后:使用偏函数,不需要再额外定义函数 >>> import functools >>> int2 = functools.partial(int, base=2)
-
# 例2:使用偏函数减少代码量 # 定义一个取余函数,默认和2取余; def mod(x,y=2): # 返回 True 或 False return x % y == 0 # 假设我们要计算和3取余,如果不使用partial()函数,那么我们每次调用mod()函数时,都要写y=3 mod(4,y=3) mod(6,y=3) # 使用partial()函数 from functools import partial mod_3 = partial(mod,y=3) mod_3(4) mod_3(6) # 此段代码源自教程评论区
-
-
小结
- 偏函数可以简化参数操作。当函数的某个参数是我们可以提前获知的,那我们就可以将它固定住!