1. 闭包
概念:在函数嵌套的前提下,内层函数引用了外层函数的变量(包括参数),外层函数又把内层函数当做返回值进行返回。这个内层函数+多引用的外层变量,称为‘闭包’。
实例1:
def test1(a):
b=10
def test2()
print(a)
print(b)
return test2
应用场景:外层函数,根据不同的参数生成不同作用功能的函数。
注意事项:
- 闭包中,如果要修改引用的外层变量,则需要使用nonlocal变量声明,否则会当做是闭包内,新定义的变量。
- 当闭包内,引用了一个后期会发生变化的变量时,一定要注意。
实例2:
def test()
funcs = []
for i in range(1, 4):
def test2():
print(i)
funcs.append(test2)
return funcs
newfuncs = test()
newfuncs[0]()
newfuncs[1]()
newfuncs[2]()
>>>3
>>>3
>>>3
以上情况的出现是因为,虽然在最后newfuncs中调用不同的test2函数,但在此之前funcs函数内的test2没有被执行,而test2函数已经被定义了3次了,其中range(1, 4)也已经走了3次了,相应的i也已经从1走到3了,所以在最后调用test2的,会去取i的值,此时i=3,故三次调用都会返回3。
案例2改进:
def test():
funcs = []
for i in range(1, 4):
def test2(num):
def inner():
print(num)
return inner
funcs.append(test2=(i))
return funcs
newfuncs = test()
newfuncs[0]()
newfuncs[1]()
newfuncs[2]()
>>>1
>>>2
>>>3
解释:在改进方案中,其实就是给之前的test2内再嵌套了一个函数inner,inner负责之前test2的功能,然后每次test2加入funcs列表的时,直接把的i传入test2,同时又不执行inner函数,之后newfuncs调用test2时,其中已经包含的每次不同的i的值,inner函数执行输出的结果也就会不同。
2. 装饰器
2.1 概念理解
装饰器本质上是一个函数,它可以让其他函数在不需要做任何改动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。
装饰器经常用于有切面需求的场景:插入日志、性能测试、事务处理、缓存、权限校验等场景。
分步理解装饰器:
(1). 需要给一个函数a()添加一个功能,功能写在decorator()
内,当我们调用'decrotor(a)`的时候就会在执行a()之前执行我们要的功能。
def a():
print('i am a')
def decorator(a):
print('新添加的功能')
a()
(2).我们需要该函数a()在业务逻辑代码中调用的时候名称还是a()(函数的单一职责性),而不是decrotor(a)
,那么最简单的方法就是在把它赋值给a,即a = decorator(a)
(3). 但是在进行a = decorator(a)
时,右边的decorator(a)
函数会被直接执行,而此时逻辑代码还没有调用它,为避免该情况发生,我们使用闭包的思想。
(4). 这里使用闭包思想:也就是decorator(a)
返回一个函数(后面我们会把这个函数写成warpper),将这个函数赋值给a,这样在进行a = decorator(a)
时右边就不会被立即执行了。最后我们把需要添加的新功能,以及a()都写在warpper
函数里,这样写就可以使得只有真正在业务逻辑代码中调用该函数的时候,warpper
才会被执行,同时我们会加上一句a = decorator(a)
,来确保业务逻辑代码中调用的时候名称还是a()。所以写出的结果如下:
def a():
print('i am a')
def decorator(a):
def warpper():
print('新添加的功能')
a()
return warpper
a = decorator(a)
a()
# 以上写法中
decorator(a) 等价于
def warpper():
print('新添加的功能')
a()
最后 Python给我们给定了以个语法糖@,即可以用@decorator
代替a = decorator(a)
,这也就演变出装饰器最终的写法:
def decorator(a):
def warpper():
print('新添加的功能')
a()
return warpper
@decorator
def a():
print('i am a')
a()
>>>新添加的功能
>>>i am a
装饰器采用了闭包的思想,在装饰函数的同时不执行函数,只有到正正的业务逻辑代码调用的时候再执行函数。
2.2 装饰器的执行时间
当@decorator出现的时候,decorator装饰器函数就立即被执行了。
2.3 装饰器的执行顺序
从上到下去装饰,从下到上去执行。
@a
@b
@c
def func():
# 等效于
func = a(b(c(func)))
2.4 对有参数的函数进行装饰
如果需要被装饰的的函数a()中有参数呢?这时候就需要有一个东西来接收函数a()中的参数,而函数a()中的参数也可能是多种多样的,这时候我们可以这样来写warpper函数:warpper(*args, **kwargs)
,但是在warpper函数中调用的的func函数也需要用:func(**args, **kwargs)
的写法来解包warpper中接收的函数,这样func才可以正常执行。代码如下:
def decorator(func):
def warpper(*args, **kwargs):
print('新添加的功能')
func(*args, **kwargs)
return warpper
@decorator
def a(i='我', am='是',):
print(i, am, "a")
a()
>>>新添加的功能
>>>我 是 a
2.5 对有返回值的函数进行装饰
如果需要被装饰的的函数a()中有return返回值呢?这时候就需要有一个东西来返回函数a()中的返回值,所以我们在warpper中也应该写入return的部分,代码如下:
def decorator(func):
def warpper(*args, **kwargs):
print('新添加的功能')
return func(*args, **kwargs)
return warpper
@decorator
def a(i='我', am='是',):
print(i, am, "a")
b = i+am+"b"
return b
b = a()
print(b)
>>>新添加的功能
>>>我 是 a
>>>我是b
原则上要保证,装饰器中的warpper函数的格式和被装饰的函数格式一致。
2.6 带有参数的装饰器
如果我们可以给装饰器本身传入一个参数,那么装饰器就可以随着传入参数的变化而做出不同的装饰功能,例如:print('新添加的功能')、print('新添加的技术')、print('新添加的颜色')。那么,该如何实现带有参数的装饰器呢? 思路:我们定义个一个外函数,把装饰器放入其中,然后装饰器中可以使用这个外函数传入的值,同时在外函数的最后返回其中的装饰器,那么这个整体就可以成为一个新的带有参数的装饰器。代码如下:
def zhuangshiqi(i):
def decorator(func):
def warpper(*args, **kwargs):
print('新添加的'+i)
return func(*args, **kwargs)
return warpper
return decorator
@zhuangshiqi(i="颜色而不是功能")
def a(i='我', am='是'):
print(i, am, "a")
return a
a()
>>>新添加的颜色而不是功能
>>>我 是 a
实例1:简单的装饰器
def decorator(func):
def wrapper(*args, *kwargs):
logging.warn("%s is running" % func.__name__)
return func(*args, *kwargs)
return wrapper
@decorator
def a():
print('i am a')
a()
>>> a is running
>>> i am a
实例2:装饰器用于验证权限的例子
userAge = 40
def canYou(func):
def decorator(*args, **kwargs):
if userAge > 1 and userAge < 10:
return func(*args, **kwargs)
print('你的年龄不符合要求,不能看')
return decorator
@canYou
def play():
print('开始播放动画片 《喜洋洋和灰太狼》')
play()
>>> 你的年龄不符合要求,不能看
内置装饰器
@staticmathod 、@classmethod 、@property
网友评论