闭包与装饰器
闭包的本质是函数嵌套,装饰器就是对闭包的应用。
1. 通过一个函数调用另一个函数
def test1 (*args,**kwargs):
print("test1")
def test2(fun,*args,**kwargs):
fun(*args, **kwargs)
print("test2")
test2(test1)
print("-----------------------------")
def test4(*args,**kwargs):
def test3(*args,**kwargs):
print("test3")
test3(args,kwargs)
print("test4")
test4()
# 通过一个函数调用另一个函数的一些方法
2.闭包的格式
def 外层函数():
def 内层函数():
pass
return 内层函数
内层函数引用 = 外层函数()
内层函数引用()
3.闭包与函数之间的区别
def out(year):
def inner():
print("内层函数调用",year)
return inner
inner_func = out(2019)
inner_func()
inner_func()
print("--------------------------------")
def func(year):
print("内层函数调用",year)
func(2020)
- 闭包和函数的格式不一样
- 外层函数可以在内存中进行保留
- 因为内层函数被引用以后,整个外层函数就不会被回收,外层函数的变量也可以得到保存,缺点是占内存
4.内层函数修改外层函数的数据
- nonlocal 声明外层函数
def out_func(year):
def inner_func():
nonlocal year # 内层函数无法直接修改外层函数,需要通过nonlocal 声明
year += 1
print("得到外层函数的参数",year)
return inner_func
func = out_func(2019)
func()
5.装饰器
def set_func(func):
def call_func(*args,**kwargs):
print('额外功能')
return func(*args,**kwargs)
return call_func
# @set_func相当于 test = set_func(test)
@set_func
def test(*args,**kwargs):
print("核心代码")
test()
权限验证
核心代码
- 装饰器就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数
- 装饰器不修改已有函数的代码,也不修改已有函数的调用方式
- 闭包函数有且只有一个参数,而且必须是函数类型,这样定义的函数才是装饰器
- 装饰器实现了即开放又封闭的写代码原则,它规定已实现的功能代码无法被修改,但可以被扩展
- @set_func 是装饰器的语法糖,相当于执行test = set_func(test) 操作,是哟个语法糖就可以方便直观的实现对已有函数的装饰
6.装饰器的简单应用场景
# 已有功能函数可以从0开始输出,一直输出到99999
def num1():
for i in range(100000):
print(i)
num1()
# 现在想为这个函数添加计算执行时间功能
# 传统方式1,这种方式会直接修改函数
import time
def num1():
start = time.time()
for i in range(100000):
print(i)
end = time.time()
print("执行时间:%s"%(end-start))
num1()
# 传统方式2,这种方式不会直接修改函数,但如果要在多个地方使用此功能会造成大量的代码冗余
import time
def num1():
for i in range(100000):
print(i)
start = time.time()
num1()
end = time.time()
print("执行时间:%s"%(end-start))
# 传统方式3,这种方式会使原函数的调用方式改变,在实际工作中会造成大量的工作量
import time
def num1():
for i in range(100000):
print(i)
def get_time():
start = time.time()
num1()
end = time.time()
print("执行时间:%s"%(end-start))
get_time()
# 此时可以使用装饰器来实现这种既不修改原函数,也不修改调用原函数的方式,还不会造成代码冗余的功能
import time
def set_func(func):
def call_func(*args,**kwargs):
start = time.time()
func()
end = time.time()
print("执行时间:%s"%(end-start))
return call_func
@set_func
def num1():
for i in range(100000):
print(i)
num1()
7.通用装饰器
def set_func(func):
def call_func(*args,**kwargs):
# 额外功能
return func(*args,**kwargs)
return call_func
@set_func
def test(*args,**kwargs):
# 原功能
- 通用装饰器可以在无论被装饰函数有没有参数,有没有返回值的情况下使用
8.一个装饰器装饰多个函数
def set_fun1(func1):
def call_fun1(*args, **kwargs):
print("用户密码验证")
return func1(*args, **kwargs)
return call_fun1
def set_fun2(func2):
def call_fun2(*args, **kwargs):
print("短信验证")
return func2(*args, **kwargs)
return call_fun2
# 装饰器一定是被装饰的函数先初始化,离被装饰函数最近的装饰器先初始化
# 1.先走@set_fun2 == > test = set_fun2(test) => test = call_fun2
# 2.再走@set_fun1 ==> test = set_fun1(test) => test = call_fun1
# @set_fun2 ==> test = set_fun2(test) ==> test = call_fun2
# test = call_fun2 ,func2 = "def test"
#@set_fun1 == > test = set_fun1(test) == > test = call_fun1
# test = call_fun1, func1 = call_fun2
# 内层函数执行的顺序是从上向下
@set_fun1
@set_fun2
def test(*args,**kwargs):
print("转帐")
print(args)
print(kwargs)
test(2,a=3)
9.带有参数的装饰器
# 添加输出日志的功能
def logging(flag):
def decorator(fn):
def inner(num1, num2):
if flag == "+":
print("--正在努力加法计算--")
elif flag == "-":
print("--正在努力减法计算--")
result = fn(num1, num2)
return result
return inner
# 返回装饰器
return decorator
# 使用装饰器装饰函数
@logging("+") # @logging("+")-->return decorator-->@decorator
def add(a, b):
result = a + b
return result
@logging("-")
def sub(a, b):
result = a - b
return result
result = add(1, 2)
print(result)
result = sub(1, 2)
print(result)
- 在装饰器外面再包裹上一个函数,让最外面的函数接收参数,返回的是装饰器,因为@符号后面必须是装饰器实例。
- 使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回是装饰器,因为 @ 符号需要配合装饰器实例使用
10.类装饰器
class Check(object):
def __init__(self, fn):
# 初始化操作在此完成
self.__fn = fn
# 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用。
def __call__(self, *args, **kwargs):
# 添加装饰功能
print("请先登陆...")
self.__fn()
@Check
def comment():
print("发表评论")
comment()
- @Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。
- 要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。
- 在call方法里进行对fn函数的装饰,可以添加额外的功能。