您好,登录后才能下订单哦!
这篇文章主要介绍了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模块编写更扁平的装饰器”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。