python:装饰器

本文深入探讨了Python装饰器的概念,通过多个示例展示了如何创建和使用装饰器,包括计算函数运行时间、获取函数名称、处理参数以及使用类装饰器。还解释了为何在装饰器中使用内置的@wraps装饰器以保持被装饰函数的元信息。最后提到了类装饰器的使用,特别是魔术方法`__init__`和`__call__`在类装饰器中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

图片

 

接下来。装逼开始....

 

实现一个简单装饰器

 

示例1:计算并输入函数运行时间

def decorator_time(func):
    def wrapper_time():
        time_data = time.perf_counter()
        results = func()
        count_time = time.perf_counter() - time_data
        print(f'函数运行时间:[{count_time :.7f}]->函数名称:[{func.__name__}]->运行结果:[{results}]')
        return results

    return wrapper_time


@decorator_time
def calculation():
    return tuple(i for i in [1, 2, 3, 4])


if __name__ == '__main__':
    print(calculation())

函数运行时间:[0.0000041]->函数名称:[calculation]->运行结果:[(1, 2, 3, 4)]
(1, 2, 3, 4)

Process finished with exit code 0

func就是前面文章所说的自由变量,results=func()就是引用自由变量

return wrapper_time 就是返回的内部函数,并取代被装饰的函数

 

工作原理

@decorator_time
def calculation():
    return tuple(i for i in [1, 2, 3, 4])
    
其实就等价于:

@decorator_time
def calculation():
    return tuple(i for i in [1, 2, 3, 4])

calculation = decorator(calculation)

为什么会这样呢?

因为calculation会作为函数参数传给decorator_time,然后decorator_time会返回wrapper_time函数,python会把wrapper_time赋值给calculation。

 

示例2:当前.py文件中获取被装饰函数的名称

if __name__ == '__main__':

    print(calculation.__name__)

wrapper_time

Process finished with exit code 0

所以。现在的calculation保存的是wrapper_time函数的引用,每次调用calculation(),执行的都是wrapper_time()

 

装饰器就是把装饰的函数替换成新的函数,二者接受相同的参数,返回被装饰的函数本该返回的值,同时还会做一些额外操作(对裸露的身体增加衣服进行修饰打扮)

 

如果不需要传参的函数,可以使用上面的装饰,遇到必须要传参的函数,就得在装饰器上增加接收参数的位置...

 

示例3:入参的装饰器,此装饰器是给原函数增加了处理参数的功能

import time
from functools import wraps


def decorator_values(func):
    @wraps(func)
    def wrapper_values(*args, **kwargs):
        """
        我是装饰器函数
        :param args:
        :param kwargs:
        :return:
        """
        time_data = time.perf_counter()
        func(*args, **kwargs)
        data_list = []
        if args:
            data_list.append(args)
        if kwargs:
            data_list.append(kwargs)
        count_time = time.perf_counter() - time_data
        print(f'函数运行时间:{count_time :.7f}')
        return data_list

    return wrapper_values


@decorator_values
def get_values(a):
    """
    我是被装饰函数
    :param a:
    :return:
    """
    return a
    
    
if __name__ == '__main__':
    print(get_values(10, b=15))
函数运行时间:0.0000028
[(10,), {'b': 15}]

*args和**kwargs是接收元组(也可以接受单参数)和字典(也可以处理关键字参数)的关键字

 

为什么要增加@wraps内置装饰器?

 

示例4:增加内置装饰器@wraps

print(get_values.__name__)
print(get_values.__doc__)
print(get_values.__code__)
get_values

    我是被装饰函数
    :param a:
    :return:
    
<code object wrapper_values at 0x7fcef8ed9d40, file "/Users/lifeng/python-projects/Test/pythonScripts/python_decorator.py", line 25>

Process finished with exit code 0

 

示例5:不增加内置装饰器@wraps

print(get_values.__name__)
print(get_values.__doc__)
print(get_values.__code__)
wrapper_values

        我是装饰器函数
        :param args:
        :param kwargs:
        :return:
        
<code object wrapper_values at 0x7f8a43dd9d40, file "/Users/lifeng/python-projects/Test/pythonScripts/python_decorator.py", line 25>

Process finished with exit code 0

从上述示例中可以看到,增加了内置装饰器@wraps,其实就是把相关属性从func上复制到wrapper_values中...

 

综上所述的都是装饰器函数,既然有函数装饰器,那肯定也会有类装饰器

 

示例6:不带参数的类装饰器

class DecoratorClass:

    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

魔术方法

  • __init__ :初始化(实例创建),也是俗称的构造方法,应该都懂,不多叙述...

  • __call__ :可调用对象

     

示例7:使用魔术方法__call__和不使用的区别

class A:

    def __init__(self):
        self.name = '小狗'

    def get_name(self):
        print(self.name)


a = A()
a.get_name()


class B:

    def __init__(self):
        self.name = '小狗'

    def __call__(self):
        print(self.name)


b = B()
b()
小狗
小狗

Process finished with exit code 0

魔术方法__call__就是简化了对象中方法的调用  (当某方法调用频率很高)...

 

示例8:带参数的类装饰器

class DecoratorValues:

    def __init__(self, code=None):
        self.code = code

    def __call__(self, func):
        @wraps(func)
        def class_wraps(*args, **kwargs):
            time_data = time.perf_counter()
            func(*args, **kwargs)
            data_list = []
            if args:
                data_list.append(args)
            if kwargs:
                data_list.append(kwargs)
            count_time = time.perf_counter() - time_data
            print(f'函数运行时间:{count_time :.7f}')
            return data_list

        return class_wraps


@DecoratorValues()
def get_values(g):
    return g
    
    
if __name__ == '__main__':
    print(get_values(10))

注意:带参数的装饰类,方块糖引用使用时千万不要忘了小括号( @DecoratorValues() ),小括号时调用,不加括号返回的是一个可调用的call对象...

 

图片

 

至此,对装饰器有了初步了解和使用,后面会继续探讨内置装饰器及如何装饰器参数化等...

 

以上总结或许能帮助到你,或许帮助不到你,但还是希望能帮助到你,如有疑问、歧义,评论区留言会及时修正发布,谢谢!

未完,待续…

一直都在努力,希望您也是

微信搜索公众号:就用python

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值