python 装饰器

62 阅读2分钟
装饰器:
    指定义一个函数,该函数是用来为其他函数添加额外的功能。
    装饰器分:有参装饰器、无参装饰器

import time

def welcomein(func):
    def wrapper(*args,**kwargs):
        li_args = list(args)
        name = li_args[0]
        print('开始对{0}的登录验证!'.format(name))
        res = func(*args,**kwargs)
        return res
    return wrapper

def outter(func): 
    def wrapper(*args, **kwargs):
        li_args = list(args)
        name = li_args[0]
        pwd = li_args[1]
        if not isinstance(name,str):
            li_args[0] = str(name)
        if not isinstance(pwd,str):
            li_args[1] = str(pwd)
        args = tuple(li_args)
        res = func(*args,**kwargs)
        return res
    return wrapper

# 装饰器会从上而下开始运行
# 由于语法糖@的限制,outter函数只能有一个参数,并且该函数只能用来接收被装饰对象的内存地址
@welcomein
@outter # log_in=outter(log_in) #log_in=>wrapper    
def log_in(name,pwd):
    name1 = input('输入账号:')
    pwd2 = input('输入密码:')
    if name1 == name and pwd2 == pwd:
        print('恭喜登录成功!3秒后自动退出!')
        for i in range(3):
            print(i+1)
            time.sleep(1)
        print('退出登录!')
    else:
        print('账号密码不匹配请重新登录!')

log_in('xxx',123)


################################## 二 #################################
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

**可以发现函数的函数名即 `__name__`已被装饰器改变**

############################# 增加@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__`的值保持不变**
    **`__name___`这个系统变量显示了当前模块执行过程中的名称,**
    
    
################################## 装饰优化 #################################
############## 普通的--时间装饰器
from functools import wraps
import time
from random import randint

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')
    return wrapper
 
@use_time
def foo():
    time.sleep(randint(1,3))

for _ in range(3):
    foo()

#输出
>>>>
foo函数use_time:1.0007495880126953s
foo函数use_time:3.0018675327301025s
foo函数use_time:3.0030477046966553s

############## 优化后的--时间装饰器
-   可以发先上面时间装饰器计算的结果,只能在控制台上打印
-   那我们怎样才能将它输出为日志呢???
-   我们需要将他的结果进行自定输出
# 在增加一层函数
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

# 改装饰器的结果就可以自定义了,下面以print函数为例
@record(print)
def foo():
    time.sleep(randint(2,5))

for _ in range(3):
    foo()
>>>>
foo 3.000645875930786
foo 4.003818988800049
foo 2.0020666122436523

## 结果输出日志:
-   只需要自己定义一个函数
def write_log(name,content):
    with open('./time.log','a')as f:
        f.write(f'{name}耗时:{content}\r\n') # \r\n 换行
        
# 只需要将装饰器改为@record(write_log)
@record(write_log)
def foo():
    time.sleep(randint(2,5))

for _ in range(3):
    foo()