Python基础 - 装饰器

目录

什么是装饰器

添加简单装饰器

语法糖

带参函数的装饰器:

带参数的装饰器:

多个装饰器

类装饰器


什么是装饰器

装饰器本质上是一个Python函数(其实就是闭包),它可以让其他函数在不需要做任何代码变动的前提下增加额外功能

装饰器能够在那个函数执行前或者执行后分别运行一些代码

装饰器的参数是一个函数,返回值也是一个函数

闭包的相关知识,参考 : Python基础 - 闭包_Melody~M的博客-CSDN博客

添加简单装饰器

有一个test函数,想给test函数执行前后分别添加一些功能(此处用打印功能代替),代码如下:

def my_decorator(func):
    def wrapper():
        print("func函数调用前")
        func()
        print("func函数调用后")
    return wrapper

def test():
    print("test函数")

if __name__ == "__main__":
    f = my_decorator(test)
    f()

打印结果:
func函数调用前
test函数
func函数调用后

创建一个 my_decorator函数作为装饰器,入参是一个函数,my_decorator内部创建一个闭包wrapper实现添加的功能,my_decorator将闭包函数进行返回

调用时f = my_decorator(test),传入待修饰的函数test,获取wrapper函数的引用并赋值给f

f()则表示执行wrapper函数

语法糖

以上代码,每次调用都需要先赋值再执行(f = my_decorator(test)),较为繁琐

python提供一种更简洁的方式定义装饰器,使用@符号

def my_decorator(func):
    def wrapper():
        print("func函数调用前")
        func()
        print("func函数调用后")
    return wrapper

@my_decorator
def test():
    print("test函数")

if __name__ == "__main__":
    test()

 使用@my_decorator这种更加简洁的方式来表示:f = my_decorator(test)

带参函数的装饰器:

如果被修饰函数带有参数,那就需要在闭包函数添加此参数,如下面的代码

def my_decorator(func):
    def wrapper(x):    #添加参数
        print("func函数调用前")
        func(x)        #添加参数
        print("func函数调用后")
    return wrapper

@my_decorator
def test(x):      #添加参数
    print("test函数")

if __name__ == "__main__":
    test(123)  

但是这个写法有一个问题,如果test函数后续有更改,比如参数个数增加,那就需要跟着修改wrapper函数,其他函数想使用这个装饰器也可能需要再去修改wrapper函数

更好的做法应该是wrapper函数不受被修饰函数的影响

def my_decorator(func):
    def wrapper(*args, **kwargs):    #添加参数
        print("func函数调用前")
        func(*args, **kwargs)        #添加参数
        print("func函数调用后")
    return wrapper

@my_decorator
def test(x):      #添加参数
    print("test函数")

if __name__ == "__main__":
    test(123)  

将wrapper的参数写成*args, **kwargs形式,利用Python的参数解包,传入任意形式的参数都可以被解析

带参数的装饰器:

希望有一个装饰器,接受一个参数num, 输入num是几就执行几次

实现如下:

def my_decorator_repeat(num):
    def my_decorator(func):
        def wrapper(*args, **kwargs):    #添加参数
            for i in range(num):
                print("func函数调用前")
                func(*args, **kwargs)        #添加参数
                print("func函数调用后")
        return wrapper
    return my_decorator

@my_decorator_repeat(num = 2)
def test(x):      #添加参数
    print(x)

if __name__ == "__main__":
    test(123)

#输出
func函数调用前
123
func函数调用后
func函数调用前
123
func函数调用后

在原来的装饰器函数外,再套一层函数,用于传入num值

wrapper函数可以访问此num,对其进行遍历

最外层函数将原来的装饰器函数引用返回,return my_decorator

通过语法糖@my_decorator_repeat(num = 2),获取到my_decorator装饰器

多个装饰器

def my_decorator1(func):
    print("my_decorator1")
    def wrapper():
        print("my_decorator1调用前")
        func()
        print("my_decorator1调用后")
    return wrapper

def my_decorator2(func):
    print("my_decorator2")
    def wrapper():
        print("my_decorator2调用前")
        func()
        print("my_decorator2调用后")
    return wrapper

@my_decorator1
@my_decorator2
def test():
    print("test函数")

if __name__ == "__main__":
    test()

执行结果如下: 

my_decorator2
my_decorator1
my_decorator1调用前
my_decorator2调用前
test函数
my_decorator2调用后
my_decorator1调用后

1、主代码运行后,依次运行@my_decorator1,@my_decorator2

2、运行到@my_decorator1,发现是一个装饰器,因此寻找my_decorator1的func函数,但@my_decorator1后面是@my_decorator2,因此开始执行@my_decorator2

3、执行@my_decorator2,寻找my_decorator2的func函数,@my_decorator2后面是test()函数,传入test函数执行my_decorator2,打印出“my_decorator2”,然后返回my_decorator2中的wrapper

4、传入test函数执行my_decorator1,打印“my_decorator1”,然后返回my_decorator1中的wrapper

类装饰器

装饰器函数必须接受一个callable对象作为参数,然后返回一个callable对象

python中,一般callable对象都是函数,但是也有例外,比如某个对象重写了call方法,这个对象也是callable的

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

    def my_decorator(self):
        print("my_decorator")

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

@Test
def test():
    print('this is test func')

test()

执行结果如下:

my_decorator
this is test func 

1、执行到@Test, 将test()函数传入Test对象,调用init方法

2、将test()指向创建的Test类对象,执行test()时,就会执行Test类对象的__call__方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值