python如何使用wrapt模块编写更扁平的装饰器
这篇文章主要介绍了python如何使用wrapt模块编写更扁平的装饰器,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。
使用 wrapt 模块编写更扁平的装饰器
在写装饰器的过程中,你有没有碰到过什么不爽的事情?不管你有没有,反正我有。我经常在写代码的时候,被下面两件事情搞得特别难受:
1. 实现带参数的装饰器时,层层嵌套的函数代码特别难写、难读
2. 因为函数和类方法的不同,为前者写的装饰器经常没法直接套用在后者上
比如,在下面的例子里,我实现了一个生成随机数并注入为函数参数的装饰器。
import random
def provide_number(min_num, max_num):
"""装饰器:随机生成一个在 [min_num, max_num] 范围的整数,追加为函数的第一个位置参数
"""
def wrapper(func):
def decorated(*args, **kwargs):
num = random.randint(min_num, max_num)
# 将 num 作为第一个参数追加后调用函数
return func(num, *args, **kwargs)
return decorated
return wrapper
@provide_number(1, 100)
def print_random_number(num):
print(num)
# 输出 1-100 的随机整数
# OUTPUT: 72
print_random_number()
@provide_number 装饰器功能看上去很不错,但它有着我在前面提到的两个问题:嵌套层级深、无法在类方法上使用。如果直接用它去装饰类方法,会出现下面的情况:
class Foo:
@provide_number(1, 100)
def print_random_number(self, num):
print(num)
# OUTPUT: <__main__.Foo object at 0x104047278>
Foo().print_random_number()
Foo 类实例中的 print_random_number 方法将会输出类实例 self ,而不是我们期望的随机数 num。
之所以会出现这个结果,是因为类方法(method)和函数(function)二者在工作机制上有着细微不同。如果要修复这个问题,provider_number 装饰器在修改类方法的位置参数时,必须聪明的跳过藏在 *args 里面的类实例 self 变量,才能正确的将 num 作为第一个参数注入。
这时,就应该是 wrapt 模块闪亮登场的时候了。wrapt 模块是一个专门帮助你编写装饰器的工具库。利用它,我们可以非常方便的改造 provide_number 装饰器,完美解决“嵌套层级深”和“无法通用”两个问题,
import wrapt
def provide_number(min_num, max_num):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
# 参数含义:
#
# - wrapped:被装饰的函数或类方法
# - instance:
# - 如果被装饰者为普通类方法,该值为类实例
# - 如果被装饰者为 classmethod 类方法,该值为类
# - 如果被装饰者为类/函数/静态方法,该值为 None
#
# - args:调用时的位置参数(注意没有 * 符号)
# - kwargs:调用时的关键字参数(注意没有 ** 符号)
#
num = random.randint(min_num, max_num)
# 无需关注 wrapped 是类方法或普通函数,直接在头部追加参数
args = (num,) + args
return wrapped(*args, **kwargs)
return wrapper
<... 应用装饰器部分代码省略 ...>
# OUTPUT: 48
Foo().print_random_number()
使用 wrapt 模块编写的装饰器,相比原来拥有下面这些优势:
• 嵌套层级少:使用 @wrapt.decorator 可以将两层嵌套减少为一层
• 更简单:处理位置与关键字参数时,可以忽略类实例等特殊情况
• 更灵活:针对 instance 值进行条件判断后,更容易让装饰器变得通用
感谢你能够认真阅读完这篇文章,希望小编分享的“python如何使用wrapt模块编写更扁平的装饰器”这篇文章对大家有帮助,同时也希望大家多多支持编程网,关注编程网行业资讯频道,更多相关知识等着你来学习!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341