跳转至

装饰器


普通装饰器

import time


def outer_func(func):
    def inner_func():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f'函数test_func运行时长: {end_time - start_time}')
    return inner_func


@outer_func
def test_func():
    time.sleep(1)

def main():
    # 重点:
    # 装饰器本质上是把被装饰的函数当作参数传入并反回一个同名函数
    # 解释:
    # 调用被装饰过的函数test_func时实际上调用outer_func(test_func)
    # 所以 test_func 就是 outer_func(test_func)
    # 因此 test_func() 就是 outer_func(test_func)()
    # outer_func(test_func)() 中的 outer_func(test_func) 反回 inner_func
    # 所以就是 inner_func()
    # inner_func() 内部可以调用外部函数的参数,也就是func,而func才是真真的test_func
    # 所以 inner_func() 内部调用了 test_func()
    test_func()


if __name__ == '__main__':
    main()

多层装饰器

def outer_func_1(func):
    def inner_func():
        print('装饰器outer_func_1运行开始')
        func()
        print('装饰器outer_func_1运行结束')
    return inner_func


def outer_func_2(func):
    def inner_func():
        print('装饰器outer_func_2运行开始')
        func()
        print('装饰器outer_func_2运行结束')
    return inner_func


@outer_func_2
@outer_func_1
def test_func():
    print("我才是test_func函数")

def main():
    test_func()


if __name__ == '__main__':
    main()

输出:

装饰器outer_func_2运行开始
装饰器outer_func_1运行开始
我才是test_func函数
装饰器outer_func_1运行结束
装饰器outer_func_2运行结束

解释:

  • 多层装饰器由外向内调用,由内向外反回

  • 根据装饰器的本质,实际执行的是 test_func = outer_func_2(outer_func_1(test_func)),分解开就是:

1、outer_func_1 = outer_func_2(outer_func_1)

2、test_func = outer_func_1(test_func)

  • 首先是过程1,调用的outer_func_1其实是outer_func_2反回的同名函数,所以实际执行的是outer_func_2函数,因此第一个输出是“装饰器outer_func_2运行开始”

其次是过程2,调用的test_func其实是outer_func_1反回的同名函数,所以实际执行的是outer_func_1函数,因此第二个输出是“装饰器outer_func_1运行开始”

然后开始执行真正的函数test_func,因此第三个输出是“我才是test_func函数”

最后是依次反回上层函数outer_func_1、outer_func_2,因此第三个输出“装饰器outer_func_1运行结束”,第四个输出“装饰器outer_func_2运行结束”

被装饰的函数有返回值

def outer_func(func):
    def inner_func():       
        # 当被装饰的函数有返回值
        # 此处不能仅仅是调用 func(), 否则不会输出 Hello World!
        # 而是需要返回调用
        return func()
    return inner_func


@outer_func
def test_func():
    return 'Hello World!'

def main():
    print(test_func())


if __name__ == '__main__':
    main()

被装饰的函数有参数

代码1

def outer_func(func):
    def inner_func(name, age):
        func(name, age)
    return inner_func


@outer_func
def introduce(name, age):
    print(f'我是{name}今年{age}岁')

def main():
    introduce('谁', '几')


if __name__ == '__main__':
    main()

代码2

def outer_func(func):
    def inner_func(*args, **kwargs):
        func(*args, **kwargs)
    return inner_func


@outer_func
def print_parmeter(a, b, *args, **kwargs):
    print(a, b)
    print(f'args:{args}')
    print(f'kwargs:{kwargs}')

def main():
    print_parmeter(1, 2, 3, 4, 5, key1='value1', key2='value2')


if __name__ == '__main__':
    main()

输出:

1 2
args:(3, 4, 5)
kwargs:{'key1': 'value1', 'key2': 'value2'}

装饰器工厂

即函数内部定义了装饰器,最后反回这个装饰器,即该函数创建了一个装饰器,所以叫装饰器工厂

def wraps_factory():
    def outer_func(func):
        def inner_func():
            func()
        return inner_func
    return outer_func


# 注意这里,后来带了括号
# 使用装饰器无需带括号,使用装饰器功能需带括号
@wraps_factory()
def test_func():
    print('Hello World')


def main():
    test_func()


if __name__ == '__main__':
    main()

上面的示例意义不大,因为用装饰器就能完成,不需要装饰器工厂

装饰器工厂的真正意义在于给装饰器携带参数,具体看下面的例子

装饰器与被装饰器都有参数

装饰器本身可以携带参数,那必定是装饰器工厂,因为只有装饰器工厂可以带参数

def wraps_factory(factory):
    def outer_func(func):
        def inner_func(parm=None):
            if parm:
                func(parm)
            else:
                # 装饰器的参数可以被内部使用
                func(factory)
        return inner_func
    return outer_func


@wraps_factory('abc')
def test_func(parm=None):
    print(f'factory name is {parm}')


def main():
    test_func()  # 等同于 wraps_factory('abc')(test_func)(parm=None)
    test_func('xyz')  # 等同于 wraps_factory('abc')(test_func)(parm='xyz')


if __name__ == '__main__':
    main()
    wraps_factory('abc')(test_func)(parm=None)
    wraps_factory('abc')(test_func)(parm='xyz')

输出:

factory name is abc
factory name is xyz

装饰器类

装饰器类需要实现两个方法:__init____call__

__init__接收一个参数,即被装饰的函数, 相当于outer_func(func)

__call__ 负责处理业务,相当于inner_func()

class Wraps:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwds):
         print('开始装饰')
         func = self.func(*args, **kwds)
         print('装饰结束')
         return func


@Wraps
def test_func(num1, num2):
    for i in range(num1, num2):
        print('Hello World')


def main():
    test_func(1,3)


if __name__ == '__main__':
    main()