11.3 函数式编程
函数式编程属于"结构化编程"的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。在函数式编程中,函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数传入另一个函数,或者作为别的函数的返回值。Python提供了部分的函数式编程。
11.3.1 函数赋值给变量
当定义一个函数的时候,函数名是一个函数类型:
>>> def my_add(a, b):
... return a + b
...
>>> my_add
<function my_add at 0x000001E4F1FB2EA0>
如果将函数本身赋值给一个变量,那么就可以使用变量来调用这个函数;
>>> f = my_add
>>> f(10, 20)
30
11.3.2 函数作为高阶函数的参数
既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
例如:
>>> def my_print(fun, x, y):
... print(fun(x, y))
...
>>> my_print(f, 10, 20)
30
11.3.2.1 Python中常见的高阶函数
map函数
map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,返回一个map对象。
例如:
>>> def f(x):
... return x ** 2
...
>>> res = map(f, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> res
<map object at 0x0000028272DBEF08>
>>> list(res)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
filter函数
filter()函数用于过滤序列。filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
例如:
>>> def is_even(x):
... return x % 2 == 0
...
>>> res = filter(is_even, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> res
<filter object at 0x0000028272DD1408>
>>> list(res)
[0, 2, 4, 6, 8]
reduce函数
reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算。
例如:
>>> from functools import reduce
>>> def f(x, y):
... return x + y
...
>>> res = reduce(f, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> res
45
sorted函数
Python内置的sorted()函数可以对list进行排序,sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
11.3.2.2 匿名函数
lambda语句可以创建一个函数对象。从本质上说,lambda需要一个参数,后跟一个表达式作为函数体,这一表达式执行的值将作为这个函数的返回值。
例如:
>>> f = lambda x: x ** 2
>>> type(f)
<class 'function'>
>>> f
<function <lambda> at 0x0000028272DB99D8>
>>> f(5)
25
这里将lambda语句创建的函数对象赋值给f,f的类型就成为了function,这时可以像使用函数一样使用f,这个函数所作的操作在lambda语句后面说明:x是参数,冒号后面的表达式x ** 2是对参数的操作,这个表达式的值就作为函数的返回值。
这种定义的函数,没有给它起函数名,叫做匿名函数。
匿名函数由于没有名字,不必担心函数名有冲突,在需要的时候,可以将它赋给一个变量,利用变量调用该函数,或者直接将匿名函数作为参数使用。
lambda函数作为参数
可以使用lambda函数改写map函数的使用,例如前面的例子可以改写为:
>>> list(map(lambda x: x ** 2, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
11.3.3 函数作为返回值
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。也就是说一个函数可以返回一个计算结果,也可以返回一个函数。
例如:11.3-函数返回值.py
# 函数返回值
def my_sum(*args):
def _sum():
res = 0
for x in args:
res += x
return res
return _sum
if __name__ == '__main__':
f = my_sum(1, 2, 3, 4, 5)
print(f)
print(f())
返回的函数并没有立刻执行,而是直到调用了f()才执行。
运行结果为:
<function my_sum.<locals>._sum at 0x000002273BDEE558>
15
11.3.3.1 装饰器
装饰器(Decorator)是一种特殊的函数,用于动态地给其他函数或类添加额外功能,而无需修改被装饰对象的源代码。例如想要在函数调用前自动打印日志,或者查看某个函数执行的时间,但是又不希望修改这个函数的定义,就可以采用装饰器实现。本质上,装饰器就是一个返回函数的高阶函数。
装饰器通过 @装饰器名称 的语法应用于函数。
装饰器的实现:11.4-装饰器.py
def log_decorator(func):
"""日志装饰器:在函数调用前后打印日志"""
def wrapper(*args, **kwargs):
# 调用前的逻辑
print(f"开始调用函数: {func.__name__}")
# 调用原函数
result = func(*args, **kwargs)
# 调用后的逻辑
print(f"函数 {func.__name__} 调用结束\n")
return result
return wrapper
# 使用装饰器
@log_decorator
def add(a, b):
return a + b
@log_decorator
def greet(name):
print(f"Hello, {name}!")
# 调用被装饰的函数
print(add(2, 3))
greet("Alice")
结果为:
开始调用函数: add
函数 add 调用结束
5
开始调用函数: greet
Hello, Alice!
函数 greet 调用结束
装饰器的工作原理:
- 装饰器 log_decorator 接收一个函数 func 作为参数
- 定义内部函数 wrapper,包含额外逻辑和对 func 的调用
- 返回 wrapper 函数替代原函数
- @log_decorator 语法等于把新定义的函数,当成了装饰器函数的输入参数,等价于 add = log_decorator(add)
带参数的装饰器
装饰器本身也可以接受参数,需要多一层嵌套。
def repeat(num):
"""带参数的装饰器:重复执行函数指定次数"""
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(num):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(num=3)
def say_hello():
print("Hello")
say_hello()
结果为:
Hello
Hello
Hello