python之什么是装饰器,如何实现一个装饰器

39 篇文章 0 订阅
32 篇文章 1 订阅

闭包的概念

  • 定义在函数内部的函数,对外部作用域的变量进行应用。

作用

  • 闭包执行完后,任然能够保持当前的运行环境(闭包中不可以修改外部函数的局部变量)
  • 闭包可以根据外部作用域的局部变量来得到不同的结果

举个例子

def func1(x):        #代码1
    def inner(y):     #代码2
        print("执行inner函数")  #代码3
        return x*y          #代码4
    print("执行fun1函数")    #代码5
    return inner    #代码6
s=func1(3)(2)  #s=6    #代码7
--------执行结果-----------
执行fun1函数
执行inner函数

执行过程分析

  1. func1(3)(2)先调用func1(3),执行到代码2,发现是函数,跳过不执行
  2. 继续执行代码5,继续执行代码6返回一个inner函数
  3. 然后调用inner(2),依次执行代码3、代码4

函数执行特点

自上而下执行、函数只有被调用时才会执行

装饰器

  • 不改变代码结构的情况下给代码添加新的功能
  • 将被装饰的函数当做参数传递给装饰器函数,并返回装饰后被装饰的函数
  • 装饰器可以是函数也可以是类,装饰器可以装饰类也可以装饰函数

举个例子

计算函数执行时间的装饰器

def count(func):
    def inner():
        starttime=datetime.now()
        func()
        endtime=datetime.now()
        print("{}的执行时间是{}".format(func.__name__,endtime-starttime))
    print("count装饰器")
    return inner
@count
def func():
    print('被装饰函数')
func() #调用func函数
----------------执行结果---------------
count装饰器
被装饰函数
func的执行时间是0:00:01.000402

添加@wraps装饰保留函数相关信息

  • 若内部函数未添加@wraps装饰,则被装饰函数的注释、函数名、注解、签名等信息会丢失
  • 解析如下:
  1. 定义一个函数可以正常输出函数的注解、函数名信息
def func2(x:int)->int:
    '''
    func2函数功能说明文档
    :param x:
    :return: **5
    '''
    return x*5
print(func2.__name__)
print(func2.__doc__)
print(func2.__annotations__)
---------------执行结果----------
func2

    func2函数功能说明文档
    :param x:
    :return: **5
    
{'x': <class 'int'>, 'return': <class 'int'>}
  1. 装饰器装饰过后执行结果
@count
def func2(x:int)->int:
    '''
    func2函数功能说明文档
    :param x:
    :return: **5
    '''
    return x*5
---------------执行结果----------
inner
None
{}
  1. 对内部函数添加@wraps装饰,如下,即可正常输出func2函数的相关信息
from functools import wraps
def count(func):
    @wraps(func)   #添加wraps装饰
    def inner():
        starttime=datetime.now()
        func()
        endtime=datetime.now()
        print("{}的执行时间是{}".format(func.__name__,endtime-starttime))
    print("count装饰器")
    return inner

定义通用装饰器

  • 意义:对于被装饰的函数,不清楚具体有什么参数,以便于装饰器可适应更多的函数
  • 实现:函数的参数定义为不定长参数(*args,**kwargs)
def count(func):
    @wraps(func)
    def inner(*args,**kwargs): #参数
        starttime=datetime.now()
        time.sleep(1)
        result=func(*args,**kwargs)   #参数
        endtime=datetime.now()
        print('{}的执行时间是:{}'.format(func.__name__,endtime-starttime) )
        return result
    print("count装饰器")
    return inner
@count
def func():
    time.sleep(1)
    print('被装饰函数')
@count
def func2(x:int)->int:
    '''
    func2函数功能说明文档
    :param x:
    :return: **5
    '''
    return x*5
print('调用func{}'.format("*"*20))
func() #调用func函数
print('调用func2{}'.format("*"*20))
func2(10)
----------------执行结果----------------
count装饰器
count装饰器
调用func********************
被装饰函数
func的执行时间是:0:00:02.000744
调用func2********************
func2的执行时间是:0:00:01.000181
  • 注意:对于被装饰的函数有return返回值,装饰器中必须要把被装饰的函数返回值进行return( result=func(*args,**kwargs)return result),调用被装饰函数时才能获取到值

带参的装饰器

  • 意义:根据不同的参数来选择不同的装饰器
  • 如下,新增加一个功能,但是只有开关开启的时候才会执行对应的代码,如果开关是关闭的则不执行
def Decor(Switch=False):
    def outer(func):
        @wraps(func)
        def inner(*args,**kwwargs):
            if Switch:
                GetInfo()
            func(*args,**kwwargs)
        return inner
    return outer

def GetInfo():
    print("开关开启时才会调用")
@Decor(Switch=True)
def func():
    print("被装饰函数")
func()
---------------执行结果--------------
开关开启时才会调用
被装饰函数

装饰器装饰类

如下,单例类(只有一个实例)的实现

def singleton(cls):
    instance={}
    def _singleton(*args,**kwargs):
        if cls not in instance:  #判断是否有实例化对象,没有则创建,有则直接返回
            instance[cls]=cls(*args,**kwargs)
        return instance[cls]
    return _singleton
@singleton
class Test():
    def __init__(self,a):
        self.a=a
x=Test(1) is Test(2)   #结果为True

等同于

class TestSingle():
    instance={}
    def __new__(cls,*args,**kwargs):
        if not cls.instance:
            cls.instance=super().__new__(cls)  #调用父类创建实例
        return cls.instance
    def __init__(self,a):
        self.a=a

类装饰器

  • 初始化的时候将被装饰对象函数传递给装饰器类
  • 调用装饰器类中的__call__()函数实现对被装饰对象的前后处理
  • 如下,func函数会被装饰成Wrapper对象
class Wrapper():
    def __init__(self,fun):
        '''说明文档111'''
        self.fun=fun
    def __call__(self,*args,**kwargs):
        start=datetime.now()
        result=self.fun(*args,**kwargs)
        end=datetime.now()
        print('{}的执行时间是:{}'.format(self.fun.__name__, end - start))
        return result
@Wrapper
def func():
    print('被装饰函数')
func()
----------执行结果----
被装饰函数
func的执行时间是:0:00:00
  • 类装饰器带参数,并且被装饰函数被装饰为函数
class Wrapper():
    def __init__(self,switch=False):
        self.switch=switch
    def __call__(self,fun):
        @wraps(fun)
        def inner(*args,**kwargs):
            if self.switch:
                start=datetime.now()
                result=fun(*args,**kwargs)
                end=datetime.now()
                print('{}的执行时间是:{}'.format(fun.__name__, end - start))
                return result
        return inner
@Wrapper(switch=True)
def func():
    '''test'''
    print('被装饰函数')
func()
------------------执行结果-----------------
被装饰函数
func的执行时间是:0:00:00.675583

类中函数实现为装饰器

  • 被装饰为函数
class Wrapper():
    def DecorOne(fun):    #此方法相当于普通函数,类名相当于命名空间,通过@Wrapper.DecorOne调用
        @wraps(fun)
        def inner(*args, **kwargs):
            starttime = datetime.now()
            time.sleep(1)
            result = fun(*args, **kwargs)
            endtime = datetime.now()
            print('{}的执行时间是:{}'.format(fun.__name__, endtime - starttime))
            return result
        return inner
@Wrapper.DecorOne
def fun():
    print('被装饰函数')
fun()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值