温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2022-03-23 10:33:30 来源:亿速云 阅读:254 作者:小新 栏目:web开发

这篇文章主要介绍了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模块编写更扁平的装饰器”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI