千家信息网

python如何使用wrapt模块编写更扁平的装饰器

发表于:2025-01-19 作者:千家信息网编辑
千家信息网最后更新 2025年01月19日,这篇文章主要介绍了python如何使用wrapt模块编写更扁平的装饰器,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。使用 wrapt
千家信息网最后更新 2025年01月19日python如何使用wrapt模块编写更扁平的装饰器

这篇文章主要介绍了python如何使用wrapt模块编写更扁平的装饰器,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

使用 wrapt 模块编写更扁平的装饰器

在写装饰器的过程中,你有没有碰到过什么不爽的事情?不管你有没有,反正我有。我经常在写代码的时候,被下面两件事情搞得特别难受:

1. 实现带参数的装饰器时,层层嵌套的函数代码特别难写、难读

2. 因为函数和类方法的不同,为前者写的装饰器经常没法直接套用在后者上

比如,在下面的例子里,我实现了一个生成随机数并注入为函数参数的装饰器。

import randomdef 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: 72print_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 wraptdef 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: 48Foo().print_random_number()

使用 wrapt 模块编写的装饰器,相比原来拥有下面这些优势:

• 嵌套层级少:使用 @wrapt.decorator 可以将两层嵌套减少为一层

• 更简单:处理位置与关键字参数时,可以忽略类实例等特殊情况

• 更灵活:针对 instance 值进行条件判断后,更容易让装饰器变得通用

感谢你能够认真阅读完这篇文章,希望小编分享的"python如何使用wrapt模块编写更扁平的装饰器"这篇文章对大家有帮助,同时也希望大家多多支持,关注行业资讯频道,更多相关知识等着你来学习!

0