Python装饰器的使用方法及原理的深入解析(详细案例版)

该文章已生成可运行项目,

一、初始装饰器

Python有很多强大又实用的功能,如果列出一个最受欢迎排行榜,那么装饰器绝对会位列其中。

初始装饰器会觉得它优雅且神奇,当真正用起来时却总觉得有些距离感,只会使用但没有真正理解,就如同深闺的冰美人一般,只能看摸不到,这往往是因为我们理解装饰器时把其他的一些概念也混杂进来了。待我揭开它的层层面纱,你会看到纯粹的装饰器其实蛮简单直率的。

二、method无参装饰器

method无参装饰器指的是装饰器函数没有放在class中,而是直接放在x.py文件内。

下面先展示一个简单的无参装饰器的例子,让大家对装饰器的定义以及使用有一个基本的了解。看完之后发现,装饰器其实就是一个函数,只不过这个函数的参数、返回值都比较特殊也是函数。
参数func对应真正要执行的函数,对于下面截图中的第65行来说,装饰器接收到的func就是test_decorator();
返回值inner_func是对真正要执行的函数进行了封装,在原函数执行前后加上一些逻辑,来为原函数增加额外的功能,相当于对原函数进行了一些装饰。

import time

def record_time_spent(func):
    def inner_func():
        start = time.time()
        func()
        end = time.time()
        print(f'call {func.__name__} func, cost time: {int(end-start)} second')
    return inner_func

@record_time_spent
def test_decorator():
    print(f'This is test_decorator()')
    time.sleep(2)

三、method含参装饰器

接下来展示一个含参装饰器的例子,通常来说含参装饰器的适用面更广,因为装饰器内部可以根据参数值执行不同的逻辑,从而覆盖更多的场景。

注意:示例代码中还包含了一种含参装饰器的错误写法,如果想通过@符号来使用装饰器的话,这种定义方式是错误的

def log_decorator(record_args=False, record_result=False):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if record_args:
                print(f'func args is: {args} {kwargs}')
            if record_result:
                result = func(*args, **kwargs)
                print(f'func result is: {result}')
                return result
            else:
                return func(*args, **kwargs)
            
        return wrapper
    return decorator

# 错误写法
def log_decorator_error(func, record_args=False, record_result=False):
    def wrapper(*args, **kwargs):
        if record_args:
            print(f'func args is: {args} {kwargs}')
        if record_result:
            result = func(*args, **kwargs)
            print(f'func result is: {result}')
            return result
        else:
            return func(*args, **kwargs)
            
    return wrapper

@log_decorator(True, True)
def multiply_log_true(x, y):
    print(f'This is multiply_log_true()')
    return x*y

@log_decorator(False, False)
def multiply_log_false(x, y):
    print(f'This is multiply_log_false()')
    return x*y

四、x.py文件内的函数是个对象

无论是放在class中的函数,还是直接放在x.py文件内的函数,它们既是函数也是对象,即object。这些对象有id、有type、有值、跟其他对象一样可以被赋值给其他变量、可以作为参数传递、也可以成为返回值。

def test_func():
    print(f'This is test_func()')

def processor(func):
    print(f'This is processor()---start')
    func()
    print(f'This is processor()---end')
    return func

五、class中的函数是个对象

通过下面两张图片对比可知:对于class中的函数,如果使用类实例来调用的话,跟第四部分的效果是一样的;如果直接用类型来调用的话,可以拿到id、类型,但是无法执行因为对于class中的函数来说缺少了self。

六、函数内部定义函数并将其返回且支持传参

下面的例子中get_multiple是一个函数,get_multiple()的返回值multiple也是一个函数。在用变量接收get_multiple()的返回值之后,可以像正常执行函数一样,通过接收返回值的变量来执行其对应的函数。

到此为止大家应该明白了,函数内定义函数并且返回值也是函数,这并不是什么神奇的东西,只是定义了一个内部函数并且把它当作返回值而已。

七、自定义函数对象

我们可以用class来构造函数对象。有成员函数__call__的class就是函数对象了,函数对象被调用时正是执行的__call__。

class DecoratorFuncObject:
    def __init__(self, name: str):
        self.name = name

    def __call__(self):
        print(f'This is DecoratorFuncObject.__call__(), name={self.name}')

八、@是个语法糖

装饰器的@是个语法糖,它没有做什么特别的事,不用它也可以实现一样的功能,只不过需要更多的代码。就拿第二部分的无参装饰器来说,我们可以对其进行改造去掉@并且效果保持不变。

在此我们参考第六部分的思路重新理解record_time_spent(func),它不过就是个函数,这个函数的参数、返回值也是函数。内部inner_func()对参数func对应的原函数进行封装,并且record_time_spent(func)返回封装后的函数inner_func,外部调用方保存好返回值后可以进行调用(对应截图中的75、76行)。

对test_no_decorator应用@record_time_spent装饰器,等价于:
new_test_no_decorator = record_time_spent(test_no_decorator)

import time

def record_time_spent(func):
    def inner_func():
        start = time.time()
        func()
        end = time.time()
        print(f'call {func.__name__} func, cost time: {int(end-start)} second')
    return inner_func

def test_no_decorator():
    print(f'This is test_no_decorator()')
    time.sleep(2)

九、用类实现无参装饰器

入参是函数对象,返回值是函数对象,如果第七部分中类的构造函数改成入参是个函数对象,不就正好符合要求了吗?接下来我们根据这个思路进行改造:

import time

'''
为了方便大家分享工作心得、交流技术问题,我创建了QQ群389591879,
大家也可以在里边相互了解各自公司的信息,希望能对大家有所帮助,同道中人欢迎加群
'''
class record_time_spent_class:
    def __init__(self, func):
        self.func = func
        print("This is record_time_spent_class.__init__()")

    def __call__(self):
        print("This is record_time_spent_class.__call__()")
        start = time.time()
        self.func()
        end = time.time()
        print(f'call {self.func.__name__} func, cost time: {int(end-start)} second')

@record_time_spent_class
def test_decorator_class():
    print(f'This is test_decorator_class()')
    time.sleep(2)

十、用类实现含参装饰器

本着精益求精的严谨态度,既然有了 "用类实现无参装饰器",那含参的情况咱们当然也要考虑,经过查阅资料找到了答案,示例代码如下。经过对比分析发现类的含参装饰器和method含参装饰器很相似,都是对函数包了一层,只是传参位置有所不同,类的含参装饰器在__init__()处传参,method含参装饰器通过装饰器函数传参。

class log_decorator_class:
    def __init__(self, record_args: bool, record_result: bool):
        self.record_args = record_args
        self.record_result = record_result
        print("This is log_decorator_class.__init__()")

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print("This is log_decorator_class.__call__()")
            if self.record_args:
                print(f'func args is: {args} {kwargs}')
            if self.record_result:
                result = func(*args, **kwargs)
                print(f'func result is: {result}')
                return result
            else:
                return func(*args, **kwargs)
        return wrapper

@log_decorator_class(True, True)
def multiply_log_class_true(x, y):
    print(f'This is multiply_log_true()')
    return x*y

十一、总结

如果各位是认真从头看到尾的话,相信对函数、装饰器都有一个比较深入的了解了,本文中的案例、截图都是经过认真思考和编排的,如果对您有帮助的话,请点赞、收藏、评论支持下,谢谢大家。

为了方便大家分享工作心得、交流技术问题,我创建了QQ群389591879,
大家也可以在里边相互了解各自公司的信息,希望能对大家有所帮助,同道中人欢迎加群。

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

changuncle

若恰好帮到您,请随心打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值