装饰器

1. 装饰器的基本知识

< 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器 >

1.1. 装饰器的概念

装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:

  1. 不能修改被装饰的函数的源代码
  2. 不能修改被装饰的函数的调用方式
  3. 满足1、2的情况下给程序增添功能

1.2. 装饰器的作用

  • 日志
  • 检查(文件是否存在,自动命名)
  • 认证
  • 计时
  • 路由
  • 发邮件

1.3. 装饰器的类型

  • 单重
  • 多重--多重的装饰器一般用不到
  • 装饰器带参数(多一层封装,传入参数)
  • 类的装饰器
  • 官方工具:wraps:保留原函数的名字和说明

1.4. 装饰器的执行次序

装饰器在运行时导入,正常函数执行时从上到下,相互调用。
在其他文件中引用这个文件,会自动执行装饰器。

2. 装饰器学习

2.1. 无参数版本装饰器


def deco_iron(func):
    def inner():  # 使用且套函数,并返回是为了解决被装饰函数运行两次的问题
        print('开始准备变身')
        func()
        print('变身结束')
    return inner

@deco_iron  # 其实就是 tony = deco_iron(tony)
def tony():
    print('toney运行函数:', tony.__name__)

# tony = deco_iron(tony)  # 这句其实就是装饰器 语法糖, 在其他代码中引用,会先运行这个

def main():
    tony()

if __name__ == '__main__':
    main()

2.2. 被装饰函数带参数版本

  1. 先装饰函数名
  2. 为嵌套函数inner传递参数-因为在最终调用的tony其本质是inner,所以在inner接受函数

# coding=utf-8

def deco_iron(func):
    def inner(*args, **kwargs):  # 使用且套函数,并返回是为了解决被装饰函数运行两次的问题
        print('开始准备变身', func.__name__)
        print(args, kwargs)
        func(*args, **kwargs)
        print('变身结束', func.__name__)
    return inner  # 被装饰的函数,其实是变成了这个,所以传递参数是传给inner

@deco_iron  # 其实就是 tony = deco_iron(tony)
def tony(name, age, dic):
    print('toney运行函数:', tony.__name__)  # 这里的name发生了变化,因为这个tony被装饰后,变成了inner,后面需要解决这个问题

# tony = deco_iron(tony)  # 这句其实就是装饰器 语法糖, 在其他代码中引用,会先运行这个

def main():
    tony('hui', 12, {'city': 'hz', 'name': 'hui'} ) # 这里的tony 其实变成了inner

if __name__ == '__main__':
    main()

2.3. 装饰器带参数版本

装饰器带参数需要在上面的版本的外面再且套一层函数

# coding=utf-8
def outer(name):
    def deco_iron(func):
        def inner(*args, **kwargs):  # 使用且套函数,并返回是为了解决被装饰函数运行两次的问题
            print('开始准备变身', func.__name__)
            print(args, kwargs)
            func(*args, **kwargs)
            print(name)
            print('变身结束', func.__name__)
        return inner  # 被装饰的函数,其实是变成了这个,所以传递参数是传给inner
    return deco_iron

@outer('小辣椒')  # 其实就是 tony = deco_iron(tony)
def tony(name, age, dic):
    print('toney运行函数:', tony.__name__)  # 这里的name发生了变化,因为这个tony被装饰后,变成了inner

# tony = deco_iron(tony)  # 这句其实就是装饰器 语法糖, 在其他代码中引用,会先运行这个

def main():
    tony('hui', 12, {'city': 'hz', 'name': 'hui'} ) # 这里的tony 其实变成了inner

if __name__ == '__main__':
    main()

2.4. 解决传递函数变化

主要使用python中的内置函数wraps。
其本质是 func.name = tony.name 其他的类似

# coding=utf-8

from functools import wraps  #
def outer(name):
    def deco_iron(func):
        @wraps(func)  #解决传递函数,函数名变化等问题'''
        def inner(*args, **kwargs):  # 使用且套函数,并返回是为了解决被装饰函数运行两次的问题
            print('开始准备变身', func.__name__)
            print(args, kwargs)
            func(*args, **kwargs)
            print(name)
            print('变身结束', func.__name__)
        return inner  # 被装饰的函数,其实是变成了这个,所以传递参数是传给inner
    return deco_iron

@outer('小辣椒')  # 其实就是 tony = deco_iron(tony)
def tony(name, age, dic):
    '''被修饰的函数'''
    print('toney运行函数:', tony.__name__)  # 这里的name发生了变化,因为这个tony被装饰后,变成了inner
    print('toney函数的doc', tony.__doc__)
# tony = deco_iron(tony)  # 这句其实就是装饰器 语法糖, 在其他代码中引用,会先运行这个

def main():
    tony('hui', 12, {'city': 'hz', 'name': 'hui'} ) # 这里的tony 其实变成了inner

if __name__ == '__main__':
    main()

2.5. 封装成类

使用技术:
  类() 调用初始化__init__
  对象() 调用__call__

3. 实际案例

原文地址:https://www.cnblogs.com/louhui/p/9230984.html