分类目录——py基础

装饰器,顾名思义,为函数额外装饰一些功能

通过这么一个例子来解释装饰器存在的意义,假使我写了几个函数来实现同一个需求,我想测试一下这个函数的效率,通过测运行耗时的方法。但是,如果在每个函数中都去写同样的测时间的操作,三五个函数还好说,一旦想测的函数多了,这个工作量就~~~

装饰器就是这样一种机制,只需要在一个函数定义时添加一个标记,就可以给该函数执行前后添加装饰的内容。

其实用方式如下例所示

import time

def timmer1(f):  # 装饰器函数
    def inner():
        start = time.time()     # time.time()获得当前时间
        ret = f()  # 执行被装饰的函数
        end = time.time()
        print(end - start)
        return ret
    return inner    # 闭包的写法

# 不用语法糖修饰的方式
# @timmer         # 语法糖 @装饰器函数名
def func1():  # 被装饰的函数
    time.sleep(0.01)    # 暂停0.01s
    print('被装饰的函数执行体')
    return '返回值'
func1 = timmer1(func1)    # 不用语法糖修饰的方式,手动实现装饰

ret1 = func1()
# 被装饰的函数执行体
# 0.011034488677978516
print(ret1)
# 返回值

# 用语法糖修饰——在函数声明前添加 @装饰器函数名 的标记
@timmer1  # 语法糖 @装饰器函数名
def func2():  # 被装饰的函数
    time.sleep(0.01)
    print('被装饰函数执行体')
    return '返回值'

# func = timmer(func)
ret2 = func2()  # 使用语法糖的形式,直接通过函数名调用即可
# 被装饰函数执行体
# 0.010964632034301758
print(ret2)
# 返回值

上面的装饰器是没有传参的方式,下例是有传参的装饰器使用方式

def timmer2(f):  # 装饰器函数
    def inner(*args, **kwargs):
        # (1,2) /(1)
        start = time.time()
        ret = f(*args, **kwargs)  # 传入动态参数,该装饰器就能兼容不同参数的函数
        end = time.time()
        print(end - start)
        return ret
    return inner

@timmer2  # 语法糖 @装饰器函数名
def func1(a, b):  # 被装饰的函数
    time.sleep(0.01)
    print('a & b', a, b)
    return a + b
ret1 = func1(1, b=2)  # inner()
# a & b 1 2
# 0.010001659393310547
print('ret1:', ret1)
# ret1: 3

def func2(a):  # 被装饰的函数
    time.sleep(0.01)
    print('this is', a)
    return a * a
func2 = timmer2(func2)  # 不使用语法糖的方式时,就需要手动把函数封装进装饰器函数

ret2 = func2(3)  # inner()
# this is 3
# 0.010001659393310547
print('ret2:', ret2)
# ret2: 9
  • 归还被装饰的函数的属性
from functools import wraps

# 在教程那个版本中,当一个函数被装饰装饰了之后,相当于执行了fun = wrapper(fun),
# fun这个函数名(fun.__name__)就不复存在了,就变成了inner
# 在inner定义之前添加@wraps(func)也是调用了一个别人封装的装饰器,为fun函数赋值他原本的函数名,当然不止改名字,还有其他的作用

def wrapper(func):  #func = holiday
    @wraps(func)  # 调用别人封装的装饰器,归还func的一些属性,通过注释与取消注释会发现func.__name__输出不同的结果
    def inner(*args,**kwargs):
        print('装饰前')
        ret = func(*args,**kwargs)
        print('装饰后')
        return ret
    return inner

@wrapper   #holiday = wrapper(holiday)
def function(num):
    '''function的注释'''
    print('function的执行, 传入的值:%d'%num)
    return 'function的返回值'

print(function.__name__)
print(function.__doc__)  # 函数注释
ret = function(3)   #inner
print(ret)
# function
# function的注释
# 装饰前
# function的执行, 传入的值:3
# 装饰后
# function的返回值
  • 带参数的装饰器

实现方式,在定义的装饰器之外再封装一个函数,用来传参数

# 带参数的装饰器——在装饰器外层再套一个
# 如果装饰了很多函数,后期可能要取消装饰,设置一个全局参数来控制是否进行装饰,就要对装饰器传参
# 而装饰器已经传了参数func(即函数名),为了再传入参数,在装饰器外层再封装一个函数
import time
FLAGE = True    # 只需通过设置这个超参数,即可控制所有被装饰器修饰的函数是否执行装饰器

def timmer_out(flag):
    def timmer(func):
        def inner(*args,**kwargs):
            if flag:
                start = time.time()
                ret = func(*args,**kwargs)
                end = time.time()
                print(end-start)
                return ret
            else:
                ret = func(*args, **kwargs)
                return ret
        return inner
    return timmer

# 装饰器实现写法1
# timmer = timmer_out(FLAGE)
# @timmer
# 装饰器实现写法2
@timmer_out(FLAGE)    #wahaha = timmer(wahaha)
def wahaha():
    time.sleep(0.1)
    print('wahahahahahaha')

@timmer_out(FLAGE)
def erguotou():
    time.sleep(0.1)
    print('erguotoutoutou')

wahaha()
erguotou()
# wahahahahahaha
# 0.10030055046081543
# erguotoutoutou
# 0.10075545310974121
  • 多个装饰器装饰同一个函数
# 一个函数被多个装饰器装饰
def wrapper1(func):
    def inner():
        print('*******************1装饰前')
        func()
        print('*******************1装饰后')
    return inner

def wrapper2(func):
    def inner():
        print('¥¥¥¥2装饰前')
        func()
        print('¥¥¥¥2装饰后')
    return inner

@wrapper1
@wrapper2
def f():
    print('f的执行体')

f()