经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Python3 » 查看文章
adorner 使用示例
来源:cnblogs  作者:顾平安  时间:2024/7/13 11:15:38  对本文有异议

模块介绍

adorner 是一个现代轻量级的 Python 装饰器辅助模块。

目前该模块仅实现了 4 个类,对应着 4 个功能:制造装饰器执行计时函数缓存捕获重试

仓库地址:https://github.com/gupingan/adorner

安装

该模块可在上方仓库中的 Releases 页面下载 tar.gz 文件后离线安装,也可以通过包管理工具进行下载安装:

  1. pip install adorner

也可以尝试下方这个命令:

  1. pip install adorner -i http://mirrors.cloud.tencent.com/pypi/simple/

或者更换为: https://mirrors.cloud.tencent.com/pypi/simple/


Decorator

Decorator 类用于标记装饰器函数,使装饰器的构造更简单。它允许你定义一个装饰器并将其应用到函数上,简化了装饰器的创建和使用过程。

源码注释

  1. class Decorator(object):
  2. def __init__(self, decorator=None):
  3. self.decorator = decorator or (lambda s: s.execute()) # 是传入的装饰器函数,如果没有传入,则默认使用一个简单的 lambda 函数,该函数调用 self.execute()。
  4. self.function = None # 用于存储被装饰的函数。
  5. # args 和 .kwargs 分别用于存储传递给被装饰函数的位置参数和关键字参数。
  6. self.args = tuple()
  7. self.kwargs = dict()
  8. def __call__(self, function):
  9. """
  10. 这里是关键部分
  11. __call__ 可使得类的实例可以像函数一样被调用。
  12. 接收一个函数 function 作为参数,并返回一个 wrapper 函数。
  13. wrapper 函数内部将被装饰的函数及其参数存储在 self.function、self.args 和 self.kwargs 中,然后调用 self.decorator(self)。
  14. functools.wraps(function) 用于保持被装饰函数的元数据(如函数名和文档字符串)
  15. """
  16. @functools.wraps(function)
  17. def wrapper(*args, **kwargs):
  18. self.function = function
  19. self.args = args
  20. self.kwargs = kwargs
  21. return self.decorator(self)
  22. return wrapper
  23. def __repr__(self):
  24. """
  25. 返回对象的字符串表示形式,便于调试和查看对象信息。根据是否有被装饰的函数来返回不同的字符串。
  26. """
  27. decorator_name = self.decorator_name.lstrip('<').rstrip('>')
  28. if self.function:
  29. return f'<{self.__class__.__name__}: {decorator_name} To {self.function.__name__}>'
  30. return f'<{self.__class__.__name__}: {decorator_name}>'
  31. def execute(self, *args, **kwargs):
  32. """
  33. 用于执行被装饰的函数。会使用传入的参数(如果有)或存储的参数来调用 _execute_sync 方法,该方法应该是为了以后适配更复杂的异步装饰器所提前编写好的
  34. """
  35. final_args = args if args else self.args
  36. final_kwargs = kwargs if kwargs else self.kwargs
  37. return self._execute_sync(final_args, final_kwargs)
  38. def _execute_sync(self, args, kwargs):
  39. """
  40. 同步地执行被装饰的函数,并返回其结果
  41. """
  42. return self.function(*args, **kwargs)
  43. @property
  44. def function_name(self):
  45. """返回被装饰函数的名称"""
  46. if self.function:
  47. return self.function.__name__
  48. return '<None>'
  49. @property
  50. def function_doc(self):
  51. """返回被装饰函数的文档字符串"""
  52. if self.function:
  53. return self.function.__doc__ or ''
  54. return ''
  55. @property
  56. def decorator_name(self):
  57. """返回装饰器的名称"""
  58. if self.decorator:
  59. return self.decorator.__name__
  60. return '<None>'
  61. @property
  62. def decorator_doc(self):
  63. """返回装饰器的文档字符串"""
  64. if self.decorator:
  65. return self.decorator.__doc__ or ''
  66. return ''

示例用法

  1. import time
  2. from adorner import Decorator
  3. @Decorator
  4. def exception_decorator(self: Decorator):
  5. """
  6. 捕获异常日志的装饰器
  7. :param self: 装饰器 Decorator 实例
  8. :return: 被修饰函数的执行结果
  9. """
  10. print(self.function_doc) # 打印被装饰函数的文档
  11. print(self.decorator_doc) # 打印装饰器的文档
  12. print(self.function_name) # 打印被装饰函数的名称
  13. print(self.decorator_name) # 打印装饰器的名称
  14. print(self.args) # 打印被装饰函数的传入的位置参数 (默认形参值不包含)
  15. print(self.kwargs) # 打印被装饰函数的传入的关键字参数 (默认形参值不包含)
  16. try:
  17. result = self.execute() # 打印 1
  18. # 执行被装饰函数,不传入任何参数时,表示使用默认的参数 self.args、self.kwargs
  19. # 可覆盖传入参数
  20. self.execute(value=2) # 打印 2
  21. self.execute(3) # 打印3 并抛出异常
  22. return result
  23. except Exception as e:
  24. print(f"捕获异常: {e}")
  25. raise
  26. @exception_decorator
  27. def risky_function(value=1):
  28. print(value)
  29. if value == 3:
  30. raise ValueError("出错了")
  31. try:
  32. risky_function()
  33. except ValueError:
  34. pass # 捕获异常: 出错了

上述示例执行后,终端应该会输出:

  1. 捕获异常日志的装饰器
  2. :param self: 装饰器 Decorator 实例
  3. :return: 被修饰函数的执行结果
  4. risky_function
  5. exception_decorator
  6. ()
  7. {}
  8. 1
  9. 2
  10. 3
  11. 捕获异常: 出错了

Timer

Timer 类是 Decorator 类的一个子类,用于测量被装饰函数的执行时间。它继承了 Decorator 类的所有功能,并在执行函数时记录开始和结束的时间,以计算函数的执行时长,该类属于 Decorator 类的扩展使用。

源码注释

  1. class Timer(Decorator):
  2. def __init__(self, decorator=None):
  3. super().__init__(decorator) # 调用父类 Decorator 的构造函数,初始化装饰器函数。
  4. self.time = 0 # 用于存储被装饰函数的执行时间。
  5. def execute(self, *args, **kwargs):
  6. """
  7. 执行被装饰的函数,并记录其执行时间。
  8. 使用 time.perf_counter() 记录开始和结束的时间,计算函数执行时长,并存储在 self.time 中。
  9. """
  10. _start = time.perf_counter() # 记录开始时间。
  11. result = super().execute(*args, **kwargs) # 调用父类的 execute 方法执行被装饰的函数。
  12. _end = time.perf_counter() # 记录结束时间。
  13. self.time = _end - _start # 计算并存储执行时间。
  14. return result # 返回被装饰函数的结果。

示例用法

下面是如何使用 Timer 类来装饰一个函数,并测量其执行时间的示例:

  1. import time
  2. from adorner import Timer
  3. timer = Timer() # 可装饰多个函数,不过不太推荐(多个函数先后执行会覆盖掉计时器的元数据)
  4. @timer
  5. def my_function(a, b):
  6. """一个简单的函数,用于演示 Timer 装饰器的使用。"""
  7. time.sleep(1) # 模拟一个耗时操作。
  8. return a + b
  9. result = my_function(1, 2)
  10. print(f'Execution result: {result}')
  11. print(f"Execution time: {timer.time} seconds")

输出将类似于:

  1. Execution result: 3
  2. Execution time: 1.0067455 seconds

Cacher

Cacher 类是一个装饰器类,用于管理和缓存函数对象及其相关数据,函数不仅仅是函数,本身也是轻量级的缓存器。

源码注释

  1. class Cacher:
  2. hash = dict() # 用于存储每个被装饰函数的 Cacher 实例。
  3. def __new__(cls, function):
  4. """
  5. 确保每个被装饰的函数只有一个 Cacher 实例。
  6. 如果该函数已经有一个 Cacher 实例,则返回该实例;
  7. 否则,创建一个新的实例,并将其存储在 hash 中。
  8. """
  9. if function in cls.hash:
  10. instance = cls.hash[function]
  11. else:
  12. instance = object.__new__(cls)
  13. instance.function = function # 设置缓存实例对应的函数
  14. instance.data = dict() # 缓存存储的结构是字典
  15. setattr(instance, '__name__', f'{cls.__name__}-{function.__name__}')
  16. cls.hash[function] = instance
  17. return instance
  18. def __call__(self, *args, **kwargs):
  19. """
  20. 使 Cacher 实例可以像函数一样被调用。
  21. 调用被装饰的函数,并返回其结果。
  22. """
  23. return self.function(*args, **kwargs)
  24. def __repr__(self):
  25. """
  26. 返回对象的字符串表示形式,便于调试和查看对象信息。
  27. """
  28. return f'<{self.__class__.__name__}: {self.function.__name__}>'
  29. def __iter__(self):
  30. """
  31. 使 Cacher 实例可迭代,迭代缓存数据。
  32. """
  33. return iter(self.data)
  34. def __contains__(self, item):
  35. """
  36. 判断缓存数据中是否包含指定的键。
  37. """
  38. return item in self.data
  39. def __add__(self, other):
  40. """
  41. 支持使用 + 运算符合并缓存数据。
  42. """
  43. if isinstance(other, self.__class__):
  44. self.data.update(other.data)
  45. return self
  46. if isinstance(other, dict):
  47. self.data.update(other)
  48. return self
  49. if isinstance(other, (tuple, list)):
  50. self.data.update(dict(other))
  51. return self
  52. raise TypeError(f'unsupported operand type(s) for +: \'{type(self)}\' and \'{type(other)}\'')
  53. def __sub__(self, other):
  54. """
  55. 支持使用 - 运算符从缓存数据中删除指定的键。
  56. """
  57. if isinstance(other, self.__class__):
  58. for key in other.data:
  59. self.data.pop(key, None)
  60. return self
  61. if isinstance(other, dict):
  62. for key in other:
  63. self.data.pop(key, None)
  64. return self
  65. if isinstance(other, (tuple, list)):
  66. self.pops(*other)
  67. return self
  68. raise TypeError(f'unsupported operand type(s) for -: \'{type(self)}\' and \'{type(other)}\'')
  69. def items(self):
  70. return self.data.items()
  71. def set(self, key, value, safe=False):
  72. """
  73. 设置缓存数据。
  74. 如果 safe 为 True,则只有在 key 不存在的情况下才设置值。
  75. """
  76. if not safe:
  77. self.data[key] = value
  78. elif key not in self.data:
  79. self.data[key] = value
  80. return self.data[key]
  81. def sets(self, **data_dict):
  82. """
  83. 批量设置缓存数据。
  84. """
  85. self.data.update(data_dict)
  86. def get(self, key, default_value=None):
  87. """
  88. 获取缓存数据。
  89. 如果 key 不存在,则返回 default_value。
  90. """
  91. return self.data.get(key, default_value)
  92. @staticmethod
  93. def _apply_filter(values, filter_function, filter_safe, filter_errors):
  94. """应用筛选函数"""
  95. def safe_filter(value):
  96. try:
  97. return filter_function(value)
  98. except filter_errors:
  99. return False
  100. filter_func = safe_filter if filter_safe else filter_function
  101. return {key: value for key, value in values.items() if filter_func(value)}
  102. @staticmethod
  103. def _apply_map(values, map_function, map_safe, map_errors):
  104. """应用遍历处理的函数"""
  105. def safe_map(value_):
  106. try:
  107. return True, map_function(value_)
  108. except map_errors:
  109. return False, None
  110. if map_safe:
  111. new_values = {}
  112. for key, value in values.items():
  113. success, mapped_value = safe_map(value)
  114. if success:
  115. new_values[key] = mapped_value
  116. return new_values
  117. else:
  118. return {key: map_function(value) for key, value in values.items()}
  119. def gets(self, *keys, default_value=None, filter_function=None, map_function=None):
  120. """
  121. 批量获取缓存数据。
  122. 支持通过 filter_function 过滤值,通过 map_function 处理值。
  123. """
  124. values = {key: self.data.get(key, default_value) for key in keys}
  125. if filter_function:
  126. filter_errors = filter_errors or (TypeError, ValueError, KeyError, IndexError)
  127. values = self._apply_filter(values, filter_function, filter_safe, filter_errors)
  128. if map_function:
  129. map_errors = map_errors or (TypeError, ValueError, KeyError, IndexError)
  130. values = self._apply_map(values, map_function, map_safe, map_errors)
  131. return values
  132. def pop(self, key, default_value=None):
  133. """
  134. 删除并返回缓存数据中的指定键。
  135. 如果键不存在,则返回 default_value。
  136. """
  137. return self.data.pop(key, default_value)
  138. def pops(self, *keys, default_value=None):
  139. """
  140. 批量删除并返回缓存数据中的指定键。
  141. 如果键不存在,则返回 default_value。
  142. """
  143. return [self.data.pop(key, default_value) for key in keys]

使用案例

下面是如何使用 Cacher 类来装饰函数,并操作缓存数据的示例:

  1. from adorner import Cacher
  2. @Cacher
  3. def example1(x):
  4. """计算乘积"""
  5. return x * x
  6. @Cacher
  7. def example2(x):
  8. """计算和"""
  9. return x + x
  10. print(example1) # 打印:<Cacher: example>
  11. # 正常调用
  12. print(example1(4)) # 打印:16
  13. # 打印函数的文档字符串
  14. print(example1.function_doc)
  15. # 缓存设置数据
  16. example1.set('a', 1)
  17. example1.set('b', 2)
  18. example1.set('c', 3)
  19. # example2.set('a', True)
  20. # example2.set('b', False)
  21. # 和上述一致
  22. example2.sets(a=True, b=False, d='数据 d')
  23. # 获取缓存数据
  24. print(example1.get('a'))
  25. print(example1.get('d', '数据不存在'))
  26. # 检查 d 是否在缓存器 example1 中
  27. print('d' in example1)
  28. # 缓存数据合并
  29. new_cacher = example1 + example2
  30. print(new_cacher.data) # 缓存器的所有数据
  31. # 打印:{'a': True, 'b': False, 'c': 3, 'd': '数据 d'}
  32. print(list(new_cacher)) # 将缓存器转为列表,可呈现存储的键
  33. new_cacher += {'e': '合并的数据 e'}
  34. # 迭代打印
  35. for k, v in new_cacher.items():
  36. print(k, v)
  37. # 批量获取数据
  38. print(new_cacher.gets('a', 'b', 'z', default_value='没有这个数据'))
  39. print(new_cacher.gets('a', 'b', 'c', filter_function=lambda x: x > 1))
  40. # 如果比较类型不一致,可能会发生错误,比如下面这个例子:
  41. # print(new_cacher.gets('a', 'b', 'c', 'd', filter_function=lambda x: x > 1))
  42. # 解决方式:你可以自行捕捉,但是那样会很繁琐,推荐使用 filter_safe 参数
  43. print(new_cacher.gets('a', 'b', 'c', 'd', filter_function=lambda x: x > 1, filter_safe=True))
  44. # 如果启用了 filter_safe 参数还无法正常捕捉,请使用 filter_errors 指定异常,默认是 (TypeError, ValueError, KeyError, IndexError)
  45. print(new_cacher.gets('a', 'b', 'c', 'd', filter_function=lambda x: x(),
  46. filter_safe=True, filter_errors=(TypeError, ValueError, KeyError, IndexError)))
  47. # 除了上述的 filter_function 参数,另外还有 map_function,同理也有 map_safe 以及 map_errors 参数
  48. print(new_cacher.gets('a', 'b', 'c', map_function=lambda x: x > 1))
  49. print(new_cacher.gets('a', 'b', 'c', 'd', map_function=lambda x: x > 1, map_safe=True))
  50. print(new_cacher.gets('a', 'b', 'c', 'd', map_function=lambda x: x > 1, map_safe=True, map_errors=(TypeError,)))
  51. # xxx_safe 参数的功能是当传入的函数执行发生异常时对应的一个处理,当出现异常时,该值对应的键值都不应存在于结果中
  52. # 优先级别:正常获取值 -> filter筛选 -> map遍历处理 -> 返回结果
  53. # 弹出某个值
  54. print(new_cacher.pop('c'))
  55. print(new_cacher.pop('c', default_value=None)) # 上面弹出了,这里尝试弹出一个不存在的,将返回 default_value(默认None)
  56. print(new_cacher.pop('c') == new_cacher.pop('c', default_value=None))
  57. print(new_cacher.data) # {'a': True, 'b': False, 'd': '数据 d', 'e': '合并的数据 e'}
  58. # 批量弹出
  59. print(new_cacher.pops('b', 'c', default_value='不存在'))
  60. print(new_cacher.data) # {'a': True, 'd': '数据 d', 'e': '合并的数据 e'}
  61. # 减法删除
  62. sub = new_cacher - [] # 支持减去 字典 {'a', 任意值} 以及元组 ('a',)
  63. print(sub.data) # {'d': '数据 d', 'e': '合并的数据 e'}
  64. print(new_cacher.data) # {'d': '数据 d', 'e': '合并的数据 e'}

Retryer

Retryer 类是一个装饰器类,用于在指定异常发生时重试被装饰函数的执行。它允许设置最大重试次数、重试间隔时间以及需要捕获的异常类型。该类为函数添加了自动重试机制,适用于网络请求、文件操作等可能会临时失败的操作。

源码注释

  1. from typing import Union, List, Type
  2. import time
  3. class Retryer:
  4. def __init__(self, max_retry: Union[int] = 3, delay: Union[int] = 0, catches: List[Type[Exception]] = None):
  5. """
  6. 初始化 Retryer 实例。
  7. :param max_retry: 最大重试次数,默认为 3。
  8. :param delay: 每次重试之间的延迟时间(秒),默认为 0。
  9. :param catches: 需要捕获的异常类型列表,默认为空列表。
  10. """
  11. self.max_retry = max_retry
  12. self.delay = delay
  13. self.catches = catches or []
  14. self.exceptions = []
  15. self.count = 0
  16. def __call__(self, function):
  17. """使 Retryer 实例可作为装饰器使用。"""
  18. return Decorator(self.run)(function)
  19. def run(self, decorator: Decorator):
  20. """执行重试逻辑。"""
  21. _catch_exceptions = tuple(self.catches) if self.catches else Exception
  22. self.exceptions.clear()
  23. i = 0
  24. while i <= self.max_retry:
  25. self.count = i
  26. try:
  27. result = decorator.execute()
  28. except _catch_exceptions as e:
  29. self.exceptions.append(e)
  30. i += 1
  31. if i <= self.max_retry:
  32. time.sleep(self.delay)
  33. continue
  34. else:
  35. return result
  36. raise self.exceptions[-1]

示例用法

下面是如何使用 Retryer 类来装饰一个函数,并在指定异常发生时重试的示例:

  1. import random
  2. from adorner import Retryer
  3. # 创建 Retryer 实例,设置捕获的异常类型为 KeyError,当被装饰的函数中出现该错误时将进行重试
  4. retryer = Retryer(catches=[KeyError])
  5. @retryer
  6. def unreliable_function():
  7. """一个可能会抛出异常的函数,用于演示 Retryer 装饰器的使用"""
  8. option = random.randint(0, 2)
  9. if option == 0:
  10. raise KeyError('Random KeyError')
  11. elif option == 1:
  12. raise ValueError('Random ValueError')
  13. else:
  14. return "Success"
  15. try:
  16. result = unreliable_function()
  17. print(result)
  18. except Exception as e:
  19. print(f"Function failed after retries: {e}")
  20. # 打印重试次数和捕获的异常
  21. print(f"Retry count: {retryer.count}")
  22. print(f"Exceptions: {retryer.exceptions}")

输出将类似于:

  1. Success
  2. Retry count: 0
  3. Exceptions: []

或在发生异常时:

  1. Function failed after retries: Random KeyError
  2. Retry count: 3
  3. Exceptions: [KeyError('Random KeyError'), KeyError('Random KeyError'), KeyError('Random KeyError')]

原文链接:https://www.cnblogs.com/gupingan/p/18299851

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号