Day10 函数3
高阶函数
高阶函数的定义:函数的参数或返回值也为函数时,这种函数称之为高阶函数。
高阶函数的特点(必须至少满足一个):**
- 接受一个或多个函数作为参数
- 将函数作为返回值返回
# 例题1:如下普通函数,是对列表list1进行筛选,找到是偶数的元素
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def fun(list1):
list2 = []
for i in list1:
if i % 2 == 0:
list2.append(i)
return list2
print(fun(list1))
# 运行结果 》》》[2, 4, 6, 8, 10]
# 更改为高阶函数
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def fun1(i):
if i % 2 == 0:
return True
def fun(fun, list1):
list2 = []
for i in list1:
if fun(i):
list2.append(i)
return list2
print(fun(fun1, list1))
#运行结果 》》》[2, 4, 6, 8, 10]
匿名函数
匿名函数,就跟他的字面意思一样,我们不对函数进行命名,因为我们在开发的时候是协同开发,如果每个人写一个函数,都对其进行一个命名的话,会非常占用命名空间,对同事也不是特别友好,因为不能取相同的函数名,不然函数将会被覆盖,所以必须将一些非常简单效果的函数写成匿名函数
匿名函数 lambda函数 就是专门用来创建一些简单的函数
lambda函数
下面是一些lambda函数示例:
lambda x, y: x*y 函数输入是x和y,输出是它们的积x*y
lambda:None 函数没有输入参数,输出是None
*lambda args: sum(args) 输入是任意个数的参数,输出是它们的和(隐性要求是输入参数必须能够进行加法运算)
**lambda kwargs: 1 输入是任意键值对参数,输出是1
实际操作(对任意2个数进行添加并输出的操作):
例题2:
print((lambda a, b: a + b)(10, 20))
运行结果 》》》30
例题3:
r = lambda a, b: a + b
print(r(10, 20))
运行结果 》》》30
filter类
filter() 需要传递两个参数 按照你设定的规则,过滤出你想要的数据
1、 传递一个函数
2 、传递一个需要过滤的序列(可迭代的)
filter类的使用:依旧是对数据筛选的更改
filter类 与 lambda函数结合使用写高阶函数
例题1的高阶函数,我们可将fun1()用lambda函数代替,再加上filter类的使用,既能简化我们的代码,没有函数重名的现象。
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = lambda i: i % 2 == 0
print(list(filter(result, list1)))
运行结果 》》》[2, 4, 6, 8, 10]
闭包
函数返回值也是函数的高阶函数也称为闭包,也就是说闭包必须有函数嵌套
闭包的定义需要满足以下三个条件:
1、在一个外函数中定义了一个内函数
2、内函数里运用了外函数的临时变量
3、并且外函数的返回值是内函数的引用
闭包的第一个特性:变量不被销毁
def func_out(num1):
def func_inner(num2): # 函数嵌套
result = num1 + num2 # 外层参数调用
print(result)
return func_inner # 将内部函数作为返回值返回
f = func_out(1) # 给num1传一个1
f(2) # 函数调用,给num2传一个2 #运行结果 》》》3
f(3) # 运行结果 》》》4
这里我们就可以看到闭包的这一特性,在f(2)运行后,并不会将num1的值销毁,所以在f(3)执行的时候,依旧可以调用num1的值
闭包的第二个特性:变量不可被更改
def func_out(num1):
def func_inner(num2): # 函数嵌套
num1 = 10
result = num1 + num2 # 外层参数调用
print(result)
print(num1) #这里就可以很清晰的看到num1的变化
func_inner(2)
print(num1)
return func_inner # 将内部函数作为返回值返回
func_out(1) # 函数对象是func_out 函数调用是func_out()
运行结果 》》》
1
12
1
首先我们这样更改,在内层函数中加上了num1=10,我们等于没有调用外层函数的参数num1,这个外层函数的num1行参与内层函数的变量num1压根就不是一个东西,所以这已经不满足闭包的条件:内部函数必须要使用到外部函数的变量,所以这个函数连闭包都不是。
闭包中让外部变量可以修改
我们先要了解一个相对的概念,如上述的num1,在整个py文件中,他是局部变量,但相对于内部函数,他就是全局变量,这里我们说的让外部变量修改就是对num1进行更改。
这里我们用到了nonlocal,也就是非本地的意思,将num1变量改成外部的参数,这样我们又形成了一个闭包,也让外部变量num1变得可有修改。
def func_out(num1):
def func_inner(num2): # 函数嵌套
# 告诉解释器,此处使用的是外部变量num1
nonlocal num1
# 这里的本意是要修改外部变量num1的值,实际上是重新进行了赋值
num1 = 10
result = num1 + num2 # 外层参数调用
print(result)
print(num1)
func_inner(2)
print(num1)
return func_inner # 将内部函数作为返回值返回
func_out(1) # 函数对象是func_out 函数调用是func_out()
运行结果 》》》
1
12
10
15、装饰器的引入
为什么要引入装饰器?
我们可以直接通过修改函数中的代码来完成需求,但是会产生以下一些问题
如果修改的函数多,修改起来会比较麻烦
不方便后期的维护
这样做会违反开闭原则(ocp)
程序的设计,要求开发对程序的扩展,要关闭对程序的修改
我们这里有一个简单的函数
def add(a, b):
return a + b
1
2
如果我们直接,修改,如下,就会违反开闭原则
不要这样使用
def fun1():
print(‘函数开始执行’)
print(‘我是fun1函数’)
print(‘函数执行完毕’)
正确使用装饰
装饰fun1()函数
def fun1():
print(‘我是fun1函数’)
def fun():
print(‘函数开始执行’)
fun1()
print(‘函数执行完毕’)
fun()
运行结果 》》》
函数开始执行
我是fun1函数
函数执行完毕
装饰add()函数
def add(a, b):
return a + b
def fun(a, b):
print(‘函数开始执行’)
result = add(a, b)
print(result)
print(‘函数执行完毕’)
fun(1, 2)
运行结果 》》》
函数开始执行
3
函数执行完毕
这样使用装饰器虽然也可以,但是我们发现每次装饰都要改很多东西,所以我们讲一下通用装饰器
def fun1():
print(‘我是fun1函数’)
def add(a, b):
return a + b
def fun(fn, *args, **kwargs):
print(‘函数开始执行’)
r = fn(*args, **kwargs)
print®
print(‘函数执行完毕’)
fun(add, 1, 2)
运行结果 》》》
函数开始执行
3
函数执行完毕
fun(fun1)
运行结果 》》》
函数开始执行
我是fun1函数
None
函数执行完毕
我们将fun函数定义几个行参,第一次是fn,可以传不同的函数,后面就是不定长参数。之后在调用的时候,实参写上我们想要传递的参数就可以辽!!!
16、装饰器的使用
前面的装饰运用只是简单介绍一下概念使用,但是他还不是正式的装饰器,因为正式的装饰器是一个闭包
通过装饰器,可以在不修改原来函数的情况下来对函数进行扩展
在开发中,我们都是通过装饰器来扩展函数的功能的
闭包条件:
函数嵌套
将内部函数作为返回值返回
内部函数必须要使用到外部函数的变量
拿上述对add()函数装饰进行修改
def add(a, b):
return a + b
def fun_out(fn, *args, **kwargs):
def fun_inner():
print('函数开始执行')
r = fn(*args, **kwargs)
print(r)
print('函数执行完毕')
return fun_inner
f = fun_out(add, 1, 2)
f()
运行结果 》》》
函数开始执行
3
函数执行完毕
写到这里肯定会产生很多问号,为啥子要闭包,还嵌套一个东西那么麻烦,结果效果还一样,就像我刚开始听完这个,其实上面那样用还没有满足我们的需求,因为我们每次需要装饰函数的时候,都得重新修改,很是麻烦,所以我们还要讲一下@的用法。
通用装饰器
def fun_out(fn, *args, **kwargs):
def fun_inner(*args, **kwargs):
print('函数开始执行')
r = fn(*args, **kwargs)
print(r)
print('函数执行完毕')
return fun_inner
需要被装饰的函数
@fun_out # 等价于 f=fun_out(fun)
def add(a, b):
return a + b
add(1, 2)
运行结果 》》》
函数开始执行
3
函数执行完毕
我们举个例子介绍一下为什么要装饰,当一个功能是公用的时候,我们将其写成装饰,就会很方便,比如我们在逛淘宝的时候,不管我们干什么,都需要进行登录,我们就可以把登录的程序写成装饰,然后我们在写其他功能的时候就可以直接@他,直接修改我们需要装饰的函数即可。就不用每写一次都修改各种参数。
课后作业
作业1. 请使用装饰器实现已存在的函数的执行所花费的时间。
time模块
简单介绍一下time模块
1、延迟功能
import time #一定要导入time模块,下面其他的就不导入了
time.sleep(秒数) # 想让程序停顿几秒钟 实现延迟功能
# 延迟功能举例
print(1)
time.sleep(2)
print(2)
# 结果,在打印了1后会停2秒,然后再打印2
2、获取时间戳
时间戳是指格林威治时间自1970年1月1日(00:00:00 GMT)至当前时间的总秒数。
import time
print(time.time())
运行结果 》》》
1612519773.541305
3、时间戳转化为元组
now_time = time.localtime(time.time())
print(now_time)
运行结果 》》》
time.struct_time(tm_year=2021, tm_mon=2, tm_mday=5, tm_hour=18, tm_min=9, tm_sec=33, tm_wday=4, tm_yday=36, tm_isdst=0)
序号 属性 值
0 tm_ year 2021(当年的年份)
1 tm_ mon 1到12 (当前的月份)
2 tm_ mday 1到31 (当前的天)
3 tm hour 0到23 (时)
4 tm_ min 0到59 (分)
5 tm sec 0到61 (60或61是闰秒) (秒)
6 tm_ wday 0到6 (0是周一) (周)
7 tm_ yday 1到366(儒略历) (一年的第多少天)
8 tm_ isdst -1, 0, 1, -1是决定是否为夏令时的旗帜
4、格式化时间
res = time.strftime("%Y-%m-%d-%H-%M-%S")
print(res)
运行结果 》》》
2021-02-05-18-47-35
符号 含义
%a 本地(locale)简化星期名称
%A 本地完整星期名称
%b 本地简化月份名称
%B 本地完整月份名称
%c 本地相应的日期和时间表示
%d 一个月中的第几天(01 - 31)
%H 一天中的第几个小时(24 小时制,00 - 23)
%l 一天中的第几个小时(12 小时制,01 - 12)
%j 一年中的第几天(001 - 366)
%m 月份(01 - 12)
%M 分钟数(00 - 59)
%p 本地 am 或者 pm 的相应符
%S 秒(01 - 61)
%U 一年中的星期数(00 - 53 星期天是一个星期的开始)第一个星期天之前的所有天数都放在第 0 周
%w 一个星期中的第几天(0 - 6,0 是星期天)
%W 和 %U 基本相同,不同的是 %W 以星期一为一个星期的开始
%x 本地相应日期
%X 本地相应时间
%y 去掉世纪的年份(00 - 99)
%Y 完整的年份
%z 用 +HHMM 或 -HHMM 表示距离格林威治的时区偏移(H 代表十进制的小时数,M 代表十进制的分钟数)
%Z 时区的名字(如果不存在为空字符)
%% %号本身
接下来我们进入实例(时间戳的使用):
作业2. 求前20个斐波那契数列所需时间
# 用以前学过的方式编写
import time
a = time.time()
# 使用函数求前20个斐波那契数列 fun(n)=fun(n-1)+fun(n-2)
def fun(n):
# 基线条件
if n <= 1:
return 1
# 递归条件
return fun(n-1)+fun(n-2)
for i in range(20):
print(fun(i), end=' ')
print() # 换行
b = time.time()
c = round((b-a),3) # 取小数点后3位并四舍五入
print(f'斐波那契数列函数运行一共花了{c}秒')
运行结果 》》》
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
斐波那契数列函数运行一共花了0.005秒
# 使用装饰器和闭包实现
import time
def get_time(fun1):
def inner():
now = time.time()
fun1()
end = time.time()
c = round((end - now), 3)
print(f'斐波那契数列函数运行一共花了{c}秒')
return inner
# 使用函数求前20个斐波那契数列 fun(n)=fun(n-1)+fun(n-2)
def fun(n):
# 基线条件
if n <= 1:
return 1
# 递归条件
return fun(n-1)+fun(n-2)
@get_time
def fun1():
list1 = []
for i in range(20):
list1.append(fun(i))
print(list1)
fun1()
运行结果 》》》
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
斐波那契数列函数运行一共花了0.005秒
本文地址:https://blog.csdn.net/zsdutm/article/details/113775196
上一篇: Ubuntu20.04安装Python3的虚拟环境教程详解
下一篇: GAN生成对抗网络