python 中的装饰器详解

装饰器


我们或多或少应该见过装饰器,比较常见的是类的声明类方法(@classmethod)和静态方法(@staticmethod),装饰器提供了一种方法,在不修改函数或类源代码的基础上,能够对函数或类指定管理或扩充代码,装饰器根据其实现方式大致分为两类,函数装饰器(用函数实现装饰功能)和类装饰器(用类实现装饰功能)。我们为什么要使用装饰器呢?虽然装饰器的功能我们通过使用其他手段也能够达到同样的效果,但是装饰器能够使函数最小化的扩展,减少了冗余性,也便于后期的维护,另外,阅读一些开源代码的时候如果遇见装饰器,我们也可以对代码有个更好的理解。装饰器的实现是通过闭包完成的,下面详细说一下闭包的概念。

闭包

闭包简单的来说就是一个函数,在该函数内部再定义一个函数,并且这个内部函数用到了外部变量(即是外部函数的参数),最终这个函数返回内部函数的引用,这就是闭包。

def decorator(par): #定义一个外部函数
    def wrapper(kkk):  #定义一个内部函数
        test = par + kkk  #内部函数使用了外部函数的参数
        print(test)
    return wrapper  #返回了内部函数的引用


if __name__ == "__main__":
    tt = decorator('mmm')
    tt('nnn')
# 输出结果
# mmmnnn   

装饰器

python 中的装饰器用@符号来表名,常见的代码是在函数名字上面@classmethod,@+函数名是python中的一种语法糖,因为解释器会对这种语法做特殊处理。

@decorator    #等价于 func = decorator(func)
def func():
    pass

下面通过代码简单说一下装饰器

def decorator(func): 
    def wrapper():
        print("装饰前------decorator---")  
        func()  
        print("装饰后------decorator---")
    return wrapper  


def decorator1(func): 
    def wrapper1():
        print("装饰前-----decorator1***********")  
        func()  
        print("装饰后-----decorator1*********")
    return wrapper1


@decorator  #等价于 test = decorator(test)
def test():
    print('test-------')


def test1():
    print('test1*******')


#等价于 test3 = decorator1(test3),括号里test3等于下面等号左边的test3
@decorator1  
@decorator  #等价于 test3 = decorator(test3)
def test3():
    print('test3&&&&&&&')

if __name__ == "__main__":
    test()
    test1 = decorator(test1)
    test1()
    test3()

输出结果如下:

装饰前------decorator---
test-------
装饰后------decorator---
装饰前------decorator---
test1*******
装饰后------decorator---
装饰前-----decorator1***********
装饰前------decorator---
test3&&&&&&&
装饰后------decorator---
装饰后-----decorator1*********
上面代码简单说明了装饰器的作用和实现原理,当有多个装饰器同时对一个函数进行装饰时,先装饰内层然后装饰外层

函数实现装饰器

函数的装饰器分为两种情况:被装饰的函数带有参数和返回值,装饰器带有参数,下面通过代码对这两种情况进行说明:

被装饰函数带有参数和返回值

def pre_decorator(par='pre_dec')
    def decorator(fun):
        def dec_fun(*args, **kwargs):  #可变参数
            print("装饰器的参数---%s" % pre)
            print("装饰前-----------")
            ret = fun(*args, **kwargs)  #函数执行的地方
            print("装饰后-----------")
            return ret  #被装饰函数没有返回值的时候为None
        return dec_fun
    return decorator

@decorator
def sum(a,b,c):
    print('执行函数')
    return a+b+c


if __name__ == "__main__":
    test = sum(2,3,6)
    print(test)

输出结果:
装饰前-----------
执行函数
装饰后-----------
11

函数装饰器带有参数

在阅读一些开源框架的代码的时候,经常会遇到装饰器带有参数,带有参数的装饰器能够进一步丰富装饰器的功能,当装饰器含有参数时,我们要想获取并利用装饰器的参数,需要在原来装饰器基础上再封装一层闭包,请看下面代码

def pre_decorator(par='pre_dec'):  # 装饰器添加一个默认字符串
    def decorator(fun):
        def dec_fun(*args, **kwargs):  # 可变参数
            print("装饰器的参数---%s" % par)
            print("装饰前-----------")
            ret = fun(*args, **kwargs)  # 函数执行的地方
            print("装饰后-----------")
            return ret  # 被装饰函数没有返回值的时候为None
        return dec_fun
    return decorator


@pre_decorator('haha')
def sum(a,b,c):
    print('执行函数')
    return a+b+c


if __name__ == "__main__":
    test = sum(2,3,6)

输出结果:
装饰器的参数---haha
装饰前-----------
执行函数
装饰后-----------

类实现装饰器

类装饰器不带有参数 和 带有参数,其实现方式有点区别

当类装饰器不带有参数

class Decorator(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print('装饰前----------')
        ret = self.func(*args, **kwargs)
        print('装饰后----------')
        return ret

# 当装饰器没有参数时,其本质还是等价于 sum = Decorator(sum),被装饰函数sum引用传到__init__()中
# 相当于实例化一个对象,对象后面加括号比如sum(), 要调用类中的__call__()
@Decorator  # 相当于sum = Decorator(sum)
def sum(a, b, c):
    print('执行函数--------')
    return a+b+c
    
if __name__ == "__main__":
    print(sum(1,2,3))

运行结果:
装饰前----------
执行函数--------
装饰后----------
6

类装饰器含有参数时

class Decorator(object):
    def __init__(self, par=''):
        self.par = par
        print('类装饰器的参数是 %s' % self.par)

    # 被装饰函数的引用传到__call__中,需要通过闭包来装饰
    def __call__(self, func):
        def dec_fun(*args, **kwargs):
            print('装饰前----------')
            ret = func(*args, **kwargs)
            print('装饰后----------')
            return ret
        return dec_fun

# 当类装饰器含有参数时,其本质等价于 sum = Decorator(par='parameter')(sum)
# 那么此时装饰器的参数par='parameter'传到了__init__()中,而被装饰函数sum的引
# 用传到了__call__()中了
@Decorator(par='parameter')  # 相当于 sum = Decorator(par='parameter')(sum)
def sum(a, b, c):
    print('执行函数--------')
    return a+b+c
    
if __name__ == "__main__":
    print(sum(1,2,3))

输出结果:
类装饰器的参数是 parameter
装饰前----------
执行函数--------
装饰后----------
6

猜你喜欢

转载自www.cnblogs.com/study-666/p/12682750.html
今日推荐