欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Day10 函数3

程序员文章站 2023-02-21 22:51:39
高阶函数高阶函数的定义:函数的参数或返回值也为函数时,这种函数称之为高阶函数。高阶函数的特点(必须至少满足一个):**接受一个或多个函数作为参数将函数作为返回值返回# 例题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...

高阶函数

高阶函数的定义:函数的参数或返回值也为函数时,这种函数称之为高阶函数。
高阶函数的特点(必须至少满足一个):**

  1. 接受一个或多个函数作为参数
  2. 将函数作为返回值返回
# 例题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个数进行添加并输出的操作):

例题2print((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