装饰器的本质

last modify
  • 装饰器(不带参数的装饰器)从实现上看,就是一个“输入和输出都是函数对象”的函数;

  • 带参数的装饰器实际上就是一个返回普通装饰器的函数(可以接受任意参数);

  • 装饰器语法实际上就是以下两种调用形式的语法糖:

    # 不带参数
    func = decorator(func)
    # 带参数
    func = decorator(*args)(func)
  • 类装饰器同理,就是一个“输入和输出都是类对象”的函数,也是类似的语法糖;

    # 不带参数
    clas = decorator(clas)
    # 带参数
    clas = decorator(*args)(clas)
  • 装饰器类:具有装饰器功能的类,通过重载 __call__ 实现;


示例 1:装饰器(不带参数)

  1. 定义装饰器

  2. 调用装饰器

  3. 等价行为

示例 2:带参数的装饰器

  1. 定义装饰器

  2. 调用装饰器

  3. 等价行为

示例 3functools.wraps 的作用

  • 在示例 2 中,打印 func.__name__ 的结果是 wrapper; 这是因为经过装饰器后,func 实际上已经指向了另一个函数;

  • 这时可以通过 functools.wraps 装饰器保留原函数的相关属性,用法如下:

    functools.wraps 本身也是一个带参数的装饰器;

示例 4:类装饰器

  • 类装饰器就是一个“输入和输出都是类对象”的函数;

  1. 定义一个类装饰器:给装饰的类添加一个 printf 函数

  2. 调用装饰器

  3. 等价行为

示例 5:装饰器类(不带参数)

  • 装饰器类:具有装饰器功能的类,通过重载 __call__ 实现;

  • 需要注意的是,该场景下不能使用 functools.wraps,而应该使用 functools.update_wrapper,详见示例;

  1. 定义装饰器类

  2. 调用装饰器类

  3. 等价行为

示例 6:带参数的装饰器类

  1. 定义装饰器类

  2. 调用装饰器类

  3. 等价行为

Last updated