Python从放弃到入门——装饰器decorator

简介

前面我们看到,在不改变原始函数定义的情况下,可以通过偏函数给函数传入指定的参数,从而简化调用。
能否不改变原函数体内容对函数的功能进行扩充呢?答案是yes,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
先看一下简单的add函数例子

def add(x,y):
    print(x,y)
    return x+y
print(add(1,2))
1 2
3

现在让我们为add增加一些功能,比如每次调用的时候输出一行语句。

def log(func):
    def wrapper(*args,**kwargs):
        print("函数%s被调用了" %func.__name__)
        return func(*args,**kwargs)#注意此处先执行func函数,再将其返回值作为wrapper的返回值
    return wrapper

@log
def add(x,y):
    print(x,y)
    return x+y
print(add(1,2))
print(add(3,4))
函数add被调用了
1 2
3
函数add被调用了
3 4
7

让我们来分析以上新增的代码。

先来看一下外层的装饰器,装饰器接收一个函数作为参数(func)。
装饰器log的内部嵌套定义了另一个函数wrapper,内函数中引用装饰器的参数(即函数func),并且装饰器返回的是内函数(wrapper),构成了一个闭包

再看内层,内函数wrapper形参定义为(*args,**kwargs)从而接收任意类型的参数。
函数体内完成新功能(即print(“函数%s被调用了” %func.name)),然后调用被装饰的函数(即 return func(*args,**kwargs))

在被装饰函数add的前面添加语句@log后,相当于执行了
add=log(add)
函数的执行流程变成了先执行装饰器内新增的功能,即 print(“函数%s被调用了” %func.name)
然后通过 return func(*args,**kwargs)执行了add函数。

被装饰函数name

函数对象有一个__name__属性,可以拿到函数的名字。
让我们来看一下被装饰后add的函数名(),此时函数名变成了wrapper。这是因为执行了add=log(add)后,同名的add变量指向了新的函数,于是调用add()将执行新函数,即在log()函数中返回的wrapper()函数。

print(add.__name__)
wrapper

如果希望被装饰函数保留原始函数名,可以在装饰器的内函数前添加另外一个装饰器:@functools.wraps(装饰器参数名)

import functools
def log1(func):
    @functools.wraps(func)
    def wrapper(*args,**kwargs):
        print("函数%s被调用了" %func.__name__)
        return func(*args,**kwargs)
    return wrapper

@log1
def add(x,y):
    print(x,y)
    return x+y
print(add1(1,2))
print(add1.__name__)
函数add1被调用了
1 2
3
add1

装饰器传参

把装饰器应用到被装饰函数时,还可以传递参数。此时需要在原装饰器上再包一层装饰器,即3层嵌套的装饰器。
此时@log2(‘2019’,‘11’,‘21’),相当于执行了语句add=log2(‘2019’,‘11’,‘21’)(add2)
上面的语句首先执行log2(‘2019’,‘11’,‘21’),返回的是decorator函数,再调用返回的函数,参数是add2函数,返回值最终是wrapper函数。

import functools
def log2(year,month,day):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args,**kwargs):
            print("%s-%s-%s,函数%s被调用了" %(year,month,day,func.__name__))
            return func(*args,**kwargs)
        return wrapper
    return decorator

@log2('2019','11','21')
def add2(x,y):
    print(x,y)
    return x+y
print(add2(1,2))


2019-11-21,函数add2被调用了
1 2
3

装饰器作为高级Python工具,能够最小化繁冗的扩展代码,并且有助于确保正确的API使用。
装饰器是设计模式的一种,被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值