一、闭包
-
闭包的概念:
在一个外函数中定义了一个内函数,内函数里使用了外函数的临时变量,
并且外函数的返回值是内函数的引用。这样就构成了一个闭包。一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。
但是闭包是一种特殊情况:
如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,
就把这个临时变量绑定给了内部函数,然后自己再结束(即外函数结束后局部变量不会被回收,会一直保留)。 -
如何创建一个闭包:
在Python中创建一个闭包可以归结为以下三点:- 闭包函数必须有内嵌函数
- 内嵌函数需要引用该嵌套函数上一级中的变量
- 闭包函数必须返回内嵌函数
-
如何判断一个函数是不是闭包
__closure__属性:
如果函数对象是一个闭包函数,那么它包含一个cell objects元组。
例如:
输出结果如下:
4. 闭包应用:
在一个函数的外部, 使用其内部的函数
避免了函数一次执行完毕后,变量被释放
例如:
# 函数内部变量a的值在outer()函数结束后,不会被释放
def outer():
a = 1
def inner():
print(a)
return inner
inn = outer()
inn()
inn()
inn()
输出结果:
2.装饰器
装饰器用来对已经封装好的函数增加新的功能,在不改动原函数的前提下,为原函数增加新的功能,同时也不改变原函数的调用方式,装饰器的本质就是一个闭包函数。
装饰器遵循原则:
开放封闭原则:
开放: 对扩展是开放的
封闭: 对修改是封闭的
- 最简单的装饰器
装饰器timer的功能是,计算被装饰函数的执行时间:
import time
def func(): # 被装饰的函数
time.sleep(0.01)
print('Hello Python')
def timer(fun): #装饰器函数(是一个闭包函数)
def inner():
start = time.time()
fun() #fun()就是被装饰的函数,它使用外部函数的参数
end = time.time()
print(end-start)
return inner
func = timer(func)
# 在不改变原函数的调用方式和内容的前提下,为原函数增加计时的功能
func()
执行顺序如下:
运行结果如下:
2. 语法糖
import time
def timer(fun):
def inner():
start = time.time()
fun()
end = time.time()
print(end-start)
return inner
@timer # 语法糖:必须位于被装饰函数的上方紧贴被装饰的函数
# @timer等价于: func = timer(func) = inner
def func():
time.sleep(0.01)
print('Hello Python')
# 有了语法糖之后,直接调用原函数能够起到与下面两行代码相同的效果
func() # 等价于直接调用 inner()
输出结果为:
3. 被装饰函数有返回值时,需要接受返回值,并将返回值返回
import time
def timer(fun):
def inner():
start = time.time()
# 被装饰函数的返回值需要这样接收
res = fun()
end = time.time()
print(end-start)
# 并将接收到的返回值返回
return res
return inner
@timer
def func():
time.sleep(0.01)
print('Hello Python')
# 当函数有返回值
return '新年好'
res = func()
print(res)
运行结果如下:
- 被装饰函数带一个参数的情况,需要在内部函数进行传参:
import time
def timer(fun):
# 参数要在内部传参
def inner(x):
start = time.time()
# 被装饰函数执行时也需要参数
res = fun(x)
end = time.time()
print(end-start)
return res
return inner
@timer
def func(a):
time.sleep(0.01)
print('Hello Python')
print('参数:', a)
return '新年好'
res = func(111)
print(res)
运行结果如下:
5. 若是装饰器需要装饰多个函数.而且被装饰的函数参数不一样多,此时需要使用可变参数来进行传参:
import time
def timer(fun):
# inner为动态参数
def inner(*args, **kwargs):
start = time.time()
# 接受的参数被封装成一个元组
# 因此被装饰的函数在执行时需要进行解包再传参
res = fun(*args, **kwargs)
end = time.time()
print('函数运行时间: ', end-start)
return res
return inner
@timer
# func只有一个参数
def func(a):
time.sleep(0.01)
print('func: Hello Python')
print('func参数:', a)
return 'func 返回: 新年好'
@timer
# func1多个参数
def func1(a,b, c='test'):
time.sleep(0.01)
print('func1: Hello Python')
print('func1参数:', a, b, c)
return 'func1 返回: 新年好'
res = func(111)
print(res)
res1 = func1(111, 222)
print(res1)
运行结果:
6. 装饰器的固定模式
综上,我们可以得出一个通用的装饰器模式:
# 定义装饰器
def wrapper(fun):
# 内部函数使用可变参数
def inner(*args, **kwargs):
"""在被装饰函数执行之前做的事"""
# 执行被装饰函数,接受参数,并接受返回值
res = fun(*args, **kwargs)
"""被装饰函数执行完之后做的事"""
# 将被装饰函数的返回值返回
return res
# 将内部函数返回
return inner
# 使用语法糖装饰被装饰函数
@wrapper
def func(a,b):
"""函数内部功能"""
return '返回值'
# 接受被装饰函数返回值
res = func(111)
print(res)