python函数式编程、闭包和装饰器

本笔记涉及函数生成器、函数式编程、闭包、高阶函数、装饰器、lambda表达式、匿名函数、偏函数等名词和语法、功能实现。
todo:map、reduce、filter、sorted

高阶函数☘

我们知道python中可以将一个变量赋值为函数名,从而实现函数调用(类似C语言中,使用指向函数的指针来调用函数)

1
2
3
f = abs
f(-10)
# 10

一个函数可以接收另外一个函数作为参数,这种函数就称为高阶函数,例如

1
2
3
4
5
6
def add(a,b):
return a+b
def m(a,b,op):
return op(a,b)
m(1,2,add)
# 3

lambda表达式(匿名函数)与函数生成器:☘

1
2
def f(x):
return x * x

等同于

1
lambda x:x*x

但是后面这种没法被后续调用(因为它没有名字),所以只能用一次并且必须立即使用,lamda表达式被广泛使用到函数式编程中,并且我记得以前在用keras编写autoencoder中在构建模型时用过与之类似的keras.layers.Lambda()即lambda层

应用☘

1.一种很不“匿名”的方法是把它赋值给一个变量,相当于又变成了一个函数

1
2
sqr=lambda x: x**2
sqr(2)

2.和map、reduce、filter、sorted等配合使用进行函数式编程

由于内容太多,暂不展开

3.“函数生成器”,将生成lambda表达式封装成函数

1
2
3
4
5
import math
def log_n(n):
return lambda x: math.log(x,n)
print(log_n(2)(2))
# 1

等效于闭包写法的惰性函数

1
2
3
4
5
6
7
import math
def log_n_lazy(x):
def log_n(n):
return math.log(x,n)
return log_n
print(log_n_lazy(2)(2))
# 1

闭包🚀️

定义🚀️

在一个内部函数中,对外部作用域的变量进行引用,(并且一般外部函数的返回值为内部函数),那么内部函数就被认为是闭包

示例🚀️

下面是一个简单的闭包示例:👀️

1
2
3
4
def outer_function(x):
def inner_function(y):
return y + x
return inner_function

创建一个闭包

1
closure = outer_function(10)

调用闭包

1
2
closure(5)
#15

实际上outer_function(10)返回了一个x=10时的inner_function函数地址,也就是说outer_function是一个可以修改函数参数的、返回值为函数的函数,从数学上讲是将一个数映射为一个函数。

接着上面的代码,修改外部函数的局部变量,再次调用闭包

1
2
3
outer_function.x = 20
closure(5)
#25

在这个示例中,outer_function返回了一个内部函数inner_function,而inner_function引用了外部函数的变量x。因此,closure就是一个闭包,它包含了对x的引用。当我们调用closure(5)时,实际上是调用了inner_function(5),它会将x的值与y相加并返回结果。由于x的值在outer_function的作用域内被修改了,因此当我们再次调用closure(5)时,x的值已经变成了20,所以最终的结果为25。

应用🚀️

从上面的案例来看,闭包能够很方便地实现生成可变参数的函数,即随时修改函数的参数或者批量生成函数参数,例如:

1
2
3
f_l = [outer_function(i) for i in range(5)]
print(f_l[3](4),f_l[1](4))
#结果: 7 5

上面首先生成了一组函数列表[x,x+1,x+2,x+3,x+4],然后调用其中的x+3和x+1函数进行计算

装饰器👨‍🦳

定义👨‍🦳

Python修饰器本质上是一个接受函数作为参数并返回一个新函数的高阶函数。哦?是一个函数到函数的算子?

示例👨‍🦳

生成一个装饰器

1
2
3
4
5
6
def my_decorator(func):
def wrapper():
print("欢迎光临!")
func()
print("欢迎下次再来!")
return wrapper

使用装饰器生成一个函数

1
2
3
@my_decorator
def say_hello():
print("客人买完东西了!")

调用生成的函数

1
2
3
4
say_hello()
# 欢迎光临!
# 客人买完东西了!
# 欢迎下次再来!

my_decorator就像一个迎宾员,在函数的前后说一些话,用他之前先@他一下,他就知道了。

应用

修饰器可以用于许多不同的目的,例如记录函数调用、验证输入或计算函数执行时间等。

计时器装饰器

虽然现在已经有很多成熟的库函数用来计时,但是下面的案例还是很有借鉴价值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import time
def timer_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.name} took {end_time - start_time:.2f} seconds to execute.")
return result
return wrapper

@timer_decorator
def slow_function():
time.sleep(5)

slow_function()

在这个示例中,timer_decorator是一个计时器装饰器,它接受一个函数作为参数并返回一个新的函数wrapper。wrapper函数记录了原始函数的执行时间并在执行完成后打印出来。使用@timer_decorator语法将装饰器应用于slow_function函数,这意味着在调用slow_function时实际上会调用wrapper函数。输出结果如下:

1
slow_function took 5.00 seconds to execute.

这个例子展示了如何使用装饰器来计算函数的执行时间。

输入验证装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def validate_input(func):
def wrapper(*args, **kwargs):
for arg in args:
if not isinstance(arg, int):
raise ValueError("All arguments must be integers.")
return func(*args, **kwargs)
return wrapper

@validate_input
def add_numbers(a, b):
return a + b

add_numbers(1, 2) # 正常运行
add_numbers(1, "2") # 抛出异常

在这个示例中,validate_input是一个输入验证装饰器,它接受一个函数作为参数并返回一个新的函数wrapper。wrapper函数检查传递给原始函数的所有参数是否都是整数,如果不是,则抛出一个ValueError异常。使用@validate_input语法将装饰器应用于add_numbers函数,这意味着在调用add_numbers时实际上会调用wrapper函数。