装饰器
1.作为Python面试中必问到的关键点—装饰器,程序开发中用的好,开发如虎添翼; 主要运用在 不能对被修饰的函数源代码进行修改, 和调用方式进行更改的情况下, 为函数添加其他功能;
2装饰器本质:使用“@函数装饰器名称”修饰原函数,等同于创建与原函数名称相同的变量,关联内嵌函数;故调用原函数时执行内嵌函数。
3.装饰器主要的作用如下:
1、引入日志
2、函数执行时间统计
3、执行函数前预备处理
4、执行函数后清理功能
5、权限校验等场景
6、缓存
4.装饰器大致流程如下:
1.执行外部函数, 原函数作为参数传递给到外层函数的形参
2.定义一个内层函数, 并且内层函数使用外层函数的变量,并调用传递进来的原函数的引用
3.外层函数的返回内层函数的引用,且调用内层函数
def 函数装饰器名称(func):
def 内嵌函数(*args, **kwargs):
需要添加的新功能
return func(*args, **kwargs)
return wrapper
@ 函数装饰器名称
def 原函数名称(参数):
函数体
原函数(参数)
5.被装饰器修饰的函数或者方法,可以是无参数的, 有参数的,不定参数的, 也可以是有返回值的, 也可以是无返回值的,也可以用作类装饰器(当然这个资源消耗较高,不建议使用), 装饰器大致的模板如下:
def set_func(func):
print("开始准备装饰器...")
def call_func(*args, **kwargs): # 这个地方的参数的可以是无参数,有参数,以及不定长参数
print("执行装饰器内部方法...")
return func(*args, **kwargs)# 这个地方的*代表的是拆包, 传递的元祖,字典内容到func方法中的,而不是元祖和字典两个参数
return call_func
@set_func # 相当于 test = set_func(test)
def test():
print("test")
test()
#运行结果:
#开始准备装饰器...
#执行装饰器内部方法...
#test
6.多个装饰器修饰一个函数的情况, 是以就近原则的, 先调用靠近原函数的外部函数,后执行靠近函数的内部函数
经典面试题 装饰器本质
def set_func_1(func):
print("开始准备装饰器1...")
def call_func(*args, **kwargs):
print("执行装饰器内部方法1...")
return func(*args, **kwargs)
return call_func
def set_func_2(func):
print("开始准备装饰器2...")
def call_func(*args, **kwargs):
print("执行装饰器内部方法2...")
return func(*args, **kwargs)
return call_func
@set_func_1
@set_func_2
def test():
print("test")
test()
#运行结果:
#开始准备装饰器2...
#开始准备装饰器1...
#执行装饰器内部方法1...
#执行装饰器内部方法2...
#test
闭包
闭包的三要素:1. 函数的嵌套 ; 2.内层函数使用外层函数的变量 ;3.外层函数返回内层函数的引用
定义:
def 外部函数名(参数):
外部变量
def 内部函数名(参数):
使用外部变量
return 内部函数名
-- 调用:
变量 = 外部函数名(参数)
变量(参数)
1、举个栗子:
# 定义一个外部函数
def test(number):
# 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
def test_in(number_in):
print("in test_in 函数, number_in is %d" % number_in)
return number+number_in
# 其实这里返回的就是闭包的结果
return test_in
# 给test函数赋值,这个20就是给参数number
ret = test(20)
# 注意这里的100其实给参数number_in
print(ret(100))
#注 意这里的200其实给参数number_in
print(ret(200))
运行结果如下:
in test_in 函数, number_in is 100
120
in test_in 函数, number_in is 200
220
2、修改闭包中外部函数的变量
在闭包内部需要使用nonlocal 关键字 来修改外部函数的变量
下面补充一下nonlocal关键字:(通过与global关键字进行对比)
第一,两者的功能不同。global关键字修饰变量后标识该变量是全局变量,对该变量进行修改就是修改全局变量,而nonlocal关键字修饰变量后标识该变量是上一级函数中的局部变量,如果上一级函数中不存在该局部变量,nonlocal位置会发生错误(最上层的函数使用nonlocal修饰变量必定会报错)。
第二,两者使用的范围不同。global关键字可以用在任何地方,包括最上层函数中和嵌套函数中,即使之前未定义该变量,global修饰后也可以直接使用,而nonlocal关键字只能用于嵌套函数中,并且外层函数中定义了相应的局部变量,否则会发生错误(见第一)。
def counter(start=0):
def incr():
nonlocal start
start += 1
return start
return incr
c1 = counter(5)
print(c1())
print(c1())
c2 = counter(50)
print(c2())
print(c2())
运行结果是:
6
7
51
52
经典面试题
def func(a):
def call_func(x):
return a * x
return call_func
flist = [func(a) for a in range(3)] # 闭包中的三个函数分别是独立的空间 ,变量a的值均不同
print(flist[0](1))
print(flist[1](1))
print(flist[2](1))
print("*" * 30)
flist = [lambda x: a * x for a in range(3)] # 匿名函数方式中变量a指向是一致的,导致a的值被覆盖,最后结果全部是2
print(flist[0](1))
print(flist[1](1))
print(flist[2](1))
# 运行结果如下:
0
1
2
******************************
2
2
2
闭包的缺点:外部变量一直存在于内存中,不会在调用结束后释放,占用内存。
第二次打印函数结果全为2的原因是 Python 闭包的延迟绑定。
这意味着内部函数被调用时,参数的值在闭包内进行查找。因此,当任何由 func()返回的函数被调用时,a的值将在附近的范围进行查找。那时,不管返回的函数是否被调用,for 循环已经完成,被赋予了最终的值 2
来源:CSDN
作者:wanglll*
链接:https://blog.csdn.net/weixin_43127295/article/details/103811428