一、基础复习
- 函数的基本用法 创建和调用函数 函数的形参与实参等等
- 函数的几种参数 位置参数、关键字参数、默认参数等
- 函数的收集参数*args **args 解包参数详解
- 函数中参数的作用域 局部作用域 全局作用域 global语句 嵌套函数 nonlocal语句等详解
- 函数的闭包(工厂函数)
(1)利用嵌套函数的外层作用具有记忆能力这个特性,让数据保存在外层函数的参数或变量中。
(2)将内层函数作为返回值给返回,这样就可以从外部间接的调用到内层的函数。
二、函数的装饰器
1.函数作为参数传递给另一个函数
例1:这里面相当于myfunc函数传递给func这个变量了吧,func()就相当于myfunc()了
>>> def myfunc():
print("正在调用myfunc...")
>>> def report(func):
print("主人,我要开始调用函数了...")
func()
print("主人,我调用完函数啦,快夸夸我^o^")
>>> report(myfunc) #函数myfunc作为参数func传递给另一个函数report
主人,我要开始调用函数了...
正在调用myfunc...
主人,我调用完函数啦,快夸夸我^o^
>>>
2.统计传入函数的运行时间
代码中f字符串的使用,可以参考f字符串
例2:
>>> import time
>>> def time_master(func):
print("开始运行程序...")
start=time.time()
func()
stop=time.time()
print("结束程序运行...")
print(f"一共耗费了{(stop-start):.2f}秒。") #.2f表示两位小数
>>> def myfunc():
time.sleep(2) #程序执行到这里什么都不干,停止两秒。
print("Hello FishC.")
>>> time_master(myfunc) #统计myfunc函数运行时间,将myfunc函数作为参数传入。
开始运行程序...
Hello FishC.
结束程序运行...
一共耗费了2.01秒。
>>>
3.装饰器、语法糖的使用
装饰器的本质1是闭包,2是拿函数当参数
艾特@加装饰器是语法糖
语法糖是特殊的语法,对于语言的功能,本身没有影响。用不用都行,有了它,程序就有更好的易用性、简洁性、可读性和便捷性。
例3:函数装饰器的使用
例2中每次想知道一个函数需要运行多长时间都要显示的去调用time_master()函数才行,更好的方案是在调用myfunc()函数时,能自觉的执行time_master()函数。
import time
def time_master(func):
def call_func():
print("开始程序运行...")
start=time.time()
func()
stop=time.time()
print("结束程序运行...")
print(f"一共耗费了{(stop-start):.2f}秒。")
return call_func
@time_master # 艾特@加装饰器是语法糖
def myfunc():
time.sleep(2)
print("I love FishC")
myfunc()
结果:
>>>
================= RESTART: E:\xiaojiayu code\046讲:函数(VI).py =================
开始程序运行...
I love FishC
结束程序运行...
一共耗费了2.04秒。
>>>
例4:和例3意思相同,但没有用装饰器
import time
def time_master(func):
def call_func():
print("开始程序运行...")
start=time.time()
func()
stop=time.time()
print("结束程序运行...")
print(f"一共耗费了{(stop-start):.2f}秒。")
return call_func
def myfunc():
time.sleep(2)
print("I love FishC")
------
myfunc=time_master(myfunc)
myfunc()
#最后一个myfunc()调用可以理解成time_master(myfunc)()。间接调用内层call_func()函数
4.多个装饰器同时用在同一个函数上
多个装饰器同时用在同一个函数上,装饰顺序按靠近函数顺序执行,执行时由外而内(即:执行顺序和装饰器顺序相反)
代码注释:装饰顺序按靠近函数顺序执行,执行时由外而内(即:执行顺序和装饰器顺序相反),先调用square、cube和add,所以结果为((2²)³)+1 。
例5:
def add(func):
def inner():
x=func()
return x+1
return inner
def cube(func):
def inner():
x=func()
return x*x*x
return inner
def square(func):
def inner():
x=func()
return x*x
return inner
# 先调用square、cube和add
@add
@cube
@square
def test():
return 2
print(test())
代码注释:
如果存在多个装饰器装饰同一个函数,那么它们的调用顺序应该是自下而上依次执行,在这个代码中,就是 @square -> @cube -> @add。
结果:
>>>
================= RESTART: E:\xiaojiayu code\046讲:函数(VI).py =================
65
>>>
5.给装饰器传递参数
代码如下:
例6:有装饰器的多层嵌套函数
import time
def logger(msg):
def time_master(func):
def call_func():
start=time.time()
func()
stop=time.time()
print(f"[{msg}]一共耗费了{(stop-start):.2f}")
return call_func
return time_master
@logger(msg="A")
def funA():
time.sleep(1)
print("正在调用funA...")
@logger(msg="B")
def funB():
time.sleep(1)
print("正在调用funB...")
funA()
funB()
结果:
>>>
================= RESTART: E:\xiaojiayu code\046讲:函数(VI).py =================
正在调用funA...
[A]一共耗费了1.03
正在调用funB...
[B]一共耗费了1.03
>>>
例7:去掉语法糖后的普通函数。
import time
def logger(msg):
def time_master(func):
def call_func():
start=time.time()
func() # 执行funA,函数当做参数进行传递
stop=time.time()
print(f"[{msg}]一共耗费了{(stop-start):.2f}")
return call_func
return time_master
def funA():
time.sleep(1)
print("正在调用funA...")
def funB():
time.sleep(1)
print("正在调用funB...")
funA=logger(msg="A")(funA) # 这里也相当于是函数闭包的深层应用,三个函数嵌套,用两个括号(msg="A")(funA)
funB=logger(msg="B")(funB)
funA()
funB()
代码解释:funA=logger(msg=“A”)(funA) 装饰器最外层需要传递一个msg=''A",得到的是time_master这个函数的引用,而我们需要的是call_func的引用,我们应该再调用一次,这里传入的是函数的名字。
课后题:
1.装饰器的作用是什么?
答:装饰器本质上也是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能。
2.装饰器必须是由嵌套函数实现吗?
答:是的,必须是。
3.如果使用装饰器去装饰一个函数,那么是否需要改动到该函数的内容呢?
答:不需要。
解析:使用装饰器,让我们可以在代码运行期间,动态地对指定函数增加一些功能(比如计算函数的运行时间、给函数添加调用日志、进行类型检查等等)。
4.所以说,装饰器本身也是一个函数,它的功能是将目标函数进行打包,并返回一个加工后的新函数对象,对吗?
答:对的。
5. 请将下面装饰器的 @ 语法糖修改为函数调用的形式?
def add(func):
def inner():
x = func()
return x + 1
return inner
def cube(func):
def inner():
x = func()
return x * x * x
return inner
def square(func):
def inner():
x = func()
return x * x
return inner
@add
@cube
@square
def test():
return 2
print(test())
答:
解法1:
def test():
return 2
test = add(cube(square(test)))
print(test())
解法2:
square=square(test)
cube=cube(square)
add=add(cube)
print(add())
解析:只要记住,多个装饰器调用的顺序是自下而上,就没问题。
题目来自小甲鱼函数(VI)