闭包
在了解装饰器@wraps之前,先需要理解一下闭包
相关资料:链接
闭包的解释
引用链接中的一段代码(为了方便解释我把源代码中的printer改成了printer1)
def make_printer(msg):
def printer():
print(msg) # 夹带私货(外部变量)
return printer # 返回的是函数,带私货的函数
printer1 = make_printer('Foo!')
printer1()
make_printer函数的返回值是printer,但是它与普通的printer不同,包含着msg信息。
当我创建printer1时,printer1表示的携带着 ‘Foo!’ 信息的printer函数,故执行printer1时最后的输出是’Foo!’。
在创建printer1时,已经形成了闭包,实际上闭包函数内定义了一个元组来存放所有的cell对象(保存了这个闭包中所有的外部变量)
>>> printer1.__closure__
(<cell at 0x00000216C8E344C8: str object at 0x00000216C8E4FB58>,)
>>> printer1.__closure__[0].cell_contents
'Foo!'
闭包的使用方式
# how to define
def wrapper(func1): # 接受一个callable对象
return func2 # 返回一个对象,一般为函数
# how to use
def target_func(args): # 目标函数
pass
# 调用方式一,直接包裹,跟上文使用的方法一样
result = wrapper(target_func)(args)
# 调用方式二,使用@语法,等同于方式一
@wrapper
def target_func(args):
pass
result = target_func()
装饰器@wraps
先来看两段代码,代码来源链接
def is_login(func):
def foo(*args,**kwargs):
return func(*args,**kwargs)
return foo
def test():
print('我是:',test.__name__)
@is_login
def test1():
print('我是:',test1.__name__)
@is_login
def test2():
print('我是:',test2.__name__)
test()
test1()
test2()
>>>>
我是: test
我是: foo
我是: foo
#增加了@wraps后
from functools import wraps
def is_login(func):
@wraps(func)
def foo(*args,**kwargs):
return func(*args,**kwargs)
return foo
def test():
print('我是:',test.__name__)
@is_login
def test1():
print('我是:',test1.__name__)
@is_login
def test2():
print('我是:',test2.__name__)
test()
test1()
test2()
>>>>
我是: test
我是: test1
我是: test2
从前两段代码可以看出@wraps可以保证装饰器修饰的函数的name的值保持不变
#代码的作用是在记录执行函数所需要的时间,并记录在日志里
from functools import wraps
import time
from random import randint
def record(output):
def use_time(func):
@wraps(func)
def wrapper(*args,**kwargs):
st_time = time.time()
result = func(*args,**kwargs)
end_time = time.time()
# print(f'{func.__name__}函数use_time:{end_time-st_time}s')
output(func.__name__, end_time-st_time)
return wrapper
return use_time
def write_log(name,content):
with open('./time.log','a')as f:
f.write(f'{name}耗时:{content}\r\n') # \r\n 换行
# 改装饰器的结果就可以自定义了,下面以write_log函数为例
@record(write_log)
def foo():
time.sleep(randint(2,5))
for _ in range(3):
foo()
可以看到在def foo() 之前有 @record(write_log),相当于先执行了record(write_log),返回了一个use_time的闭包,带着一个cell对象(write_log)。
现在相当于def foo() 之前的是 @use_time了,使用@wraps(func)来保证foo的名字不被改变,return回来一个wrapper的闭包,相当于现在调用foo,其实调用的是携带着foo信息,以及write_log的一个wrapper的闭包,foo里的参数转换成了(*args,**kwargs)。
大概的理解就是这样,如果有什么不对的,请大家及时指出,初学python的装饰器,若理解不当,还请见谅。
最后贴上一个线程安全的单例装饰器
from functools import wraps
from threading import Lock
def singleton(cls):
"""线程安全的单例装饰器"""
instances = {}
locker = Lock()
@wraps(cls)
def wrapper(*args, **kwargs):
if cls not in instances:
with locker:
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
相关链接:
学习源头——https://github.com/jackfrued/Python-100-Days/blob/master/Day16-20/16-20.Python语言进阶.md
Python的装饰器——https://www.jianshu.com/p/0135fe1f5131
@wraps的使用——https://www.jianshu.com/p/5df1769e562e
闭包的使用——https://betacat.online/posts/2016-10-23/python-closure/