Python——装饰器@wraps

Python——装饰器@wraps

  • 闭包
    • 闭包的解释
    • 闭包的使用方式
  • 装饰器@wraps

闭包

在了解装饰器@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/

你可能感兴趣的:(Python基础知识,@wraps,闭包,装饰器,Python)