• Pyhton 装饰器的作用


    1. 定义

    在python中,常常看到@符号,后面的函数就是装饰器,比如在定义一个类的时候,会用到@property,作用是将一个方法转换为类的属性,也是一个装饰器。装饰器可以理解为对函数外加一个行为,这个行为是对你的项目代码是比较有意义的通用行为,常见的行为有打印日子,打印函数计算时长,也比如本次我们要做的参数检查等。对函数加装饰器,不仅可以增加函数功能,也能简化代码,提高可读性。

    另外,python 内置了3种函数装饰器,分别是 @staticmethod、@classmethod 和 @property,其中 staticmethod()、classmethod() 和 property() 都是 Python 的内置函数。

    作用1 装饰器

    例子:计算函数的运行时长,

    1. import time
    2. def timer(func):
    3. """
    4. 用于计时的装饰器函数
    5. :param func: 被装饰函数
    6. :return: 闭包函数,封装了自定义行为与被装饰函数的调用
    7. """
    8. def wrapper(*args, **kwargs):
    9. """
    10. 闭包函数
    11. :param args: 被装饰函数的位置参数
    12. :param kwargs: 被装饰函数的关键字参数
    13. :return: int,被装饰函数的计算结果
    14. """
    15. t1 = time.time() # 开始时间
    16. r = func(*args, **kwargs) # 被装饰函数调用
    17. t2 = time.time() # 结束时间
    18. cost = t2 - t1 # 计算时长
    19. print('function cost{} second'.format(cost))
    20. return r
    21. return wrapper

    我们定义的函数timer是一个装饰器函数(用来包装的,放在最外面的),这个函数比较特殊,它能把函数作为参数传入,这里是我们的被装饰函数(与装饰函数区分),然后返回的对象也为一个函数,称之为闭包函数,在闭包函数中,需要做两件事,一件事是定义行为(打印函数计算时长),另一件事是调用函数计算。

    装饰器函数写好后,可以直接使用@符号进行使用,我们定义一个需要被装饰的函数add,作用就是对两个数相加,使用装饰器时在函数前一行加上@timer即可。

    1. @timer
    2. def add(x, y):
    3. time.sleep(2)
    4. return x + y

    @符号是装饰器的语法糖,可以理解为装饰器的快捷键,如果不用@符号,直接用装饰器函数调用的形式也是可以的,只不过语法糖更简洁。@符号的作用就是在函数实际调用前,执行以下语句:

    1. add = timer(add)
    2. print(add(1, 2))
    3. #function cost2.000091075897217 second 3

     调用add 被装饰函数,会把该被装饰函数作为参数传给装饰函数,类似与add(1,2),这里会先调用装饰函数,然后在装饰函数里面执行func

    作用2 参数检查

    顾名思义该部分的@用来进行参数检查,比如

    首先要定义一个参数检查装饰器,用来检查输入参数与定义是否一致,否则报错

    1. import collections
    2. import functools
    3. import inspect
    4. def para_check(func):
    5. """
    6. 函数参数检查装饰器,需要配合函数注解表达式(Function Annotations)使用
    7. """
    8. msg = 'Argument {argument} must be {expected!r},but got {got!r},value {value!r}'
    9. # 获取函数定义的参数
    10. sig = inspect.signature(func)
    11. parameters = sig.parameters # 参数有序字典
    12. arg_keys = tuple(parameters.keys()) # 参数名称
    13. #内部装饰器,显示调用函数的原名
    14. @functools.wraps(func)
    15. def wrapper(*args, **kwargs):
    16. CheckItem = collections.namedtuple('CheckItem', ('anno', 'arg_name', 'value'))
    17. check_list = []
    18. # collect args *args 传入的参数以及对应的函数参数注解,即x:init,
    19. for i, value in enumerate(args):
    20. arg_name = arg_keys[i]
    21. anno = parameters[arg_name].annotation
    22. check_list.append(CheckItem(anno, arg_name, value))
    23. # collect kwargs **kwargs 传入的参数以及对应的函数参数注解
    24. for arg_name, value in kwargs.items():
    25. anno = parameters[arg_name].annotation
    26. check_list.append(CheckItem(anno, arg_name, value))
    27. # check type
    28. for item in check_list:
    29. if not isinstance(item.value, item.anno):
    30. error = msg.format(expected=item.anno, argument=item.arg_name,
    31. got=type(item.value), value=item.value)
    32. raise TypeError(error)
    33. return func(*args, **kwargs)
    34. return wrapper

    作用3 注释表达式

    注释表达式是python的一种特性,使可以在函数定义的时候,顺便定义输入参数的类型,提高函数可读性。用:符号加上参数类型实现

    1. def anno(x: str, y: dict, z: int = 123):
    2. print("x type:{},y type:{},z type{}".format(type(x), type(y), type(z)))
    3. anno('123', {'a': 1}, 2)
    4. #x type:,y type:,z type

    在参数类型中用:来定义参数类型

  • 相关阅读:
    ESP8266智能家居(4)——开发APP基础篇
    [发送AT指令配置a7670C模块上网]
    【深入解析spring cloud gateway】08 Reactor 知识扫盲
    对于多分类问题,使用深度学习(Keras)进行迁移学习提升性能
    Pandas读取文件性能优化 Tricks
    【校招VIP】产品深度理解之热点事件分析
    Testing Library - About Queries
    【LeetCode-98】
    这5个AI智能绘画工具真的太强大了,绝对不输Midjourney。
    药品和药企信息接口
  • 原文地址:https://blog.csdn.net/baidu_31437863/article/details/126151236