python装饰器详解

装饰器的作用就是在不改变原函数的基础上,动态的扩展函数的功能。

装饰器本质就是返回一个函数的高阶函数,那么什么是高阶函数?一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。举个栗子:

比如我们现在有一个执行登录的函数login(),但是在登录之前我们需要一个验证函数validate()对用户进行验证。我们可以这么写:

def validate(func):
    print("进行验证")
    func()

def login():
    print("用户登录")

validate(login)

##运行结果:
进行验证
用户登录

此处validate函数就是一个高阶函数。但是这么写,我们虽然没有对原函数进行修改,但是我们调用函数的时候需要这么调用:validate(login),当有很多函数都需要验证时,我们需要修改所有函数的调用接口。那能不能在不改变原来的调用方式,调用login()时就能实现上面的功能呢?当然可以,请看下面:

1.最简单的装饰器

def validate(func):
    def wrapper():
        print("进行验证")
        func()
    return wrapper

@validate
def login():
    print("用户登录")

login()

##运行结果:
进行验证
用户登录

这样我们就实现上面的需求了,这是一个最简单的装饰器的例子。

当我们在login函数上面加上@validate时,就完成了函数的装载,相当于执行了validate(login)(外函数)。但是执行validate(login)只是返回了wrapper函数(内函数),将wrapper指向的函数赋值给了login。值得注意的是:我们可以将函数名看成是变量,它指向真正的函数对象。当我们再调用login函数时,实际执行的是wrapper函数。

## 装饰器会改变原函数的一些属性值,例如__name__属性值

def validate(func):
    def wrapper():
        print("进行验证")
        func()
    return wrapper

@validate
def login():
    print("用户登录")

print(login.__name__)

##运行结果:
wrapper

解决办法:python为我们提供了一个装饰器functools.wraps。改进后的代码:

from functools import wraps

def validate(func):
    @wraps(func)
    def wrapper():
        print("进行验证")
        func()
    return wrapper

@validate
def login():
    print("用户登录")

print(login.__name__)

## 运行结果:
login

2.兼容被装饰函数的参数和返回值(完整)

from functools import wraps

def validate(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("进行验证")
        res = func(*args, **kwargs)
        return res
    return wrapper

@validate
def login(auth):
    print("用户登录: %s" %(auth))
    return "haha"

res = login("vip")
print(res)

## 运行结果:
进行验证
用户登录: vip

3.带参数的装饰器

from functools import wraps

def validate(auth):
    def outter_wrapper(func):
        @wraps(func)
        def inner_wrapper(*args, **kwargs):
            if auth == "vip": 
                print("进行登录验证")
                func(*args, **kwargs)
            else:
                print("权限不够")
        return inner_wrapper
    return outter_wrapper

@validate("not_vip")  # vip
def login():
    print("用户登录")

login()

## 运行结果
权限不够

函数声明时就会完成装饰器的装载,相当于login=validate("not_vip").(login)。

4.多个装饰器

from functools import wraps

def dec01(func):  
    print("1111") 
    @wraps(func) 
    def wrapper01():  
        print("2222")  
        func()  
        print("3333")  
    return wrapper01  

def dec02(func):  
    print("aaaa")
    @wraps(func)  
    def wrapper02():  
        print("bbbb")  
        func()  
        print("cccc")  
    return wrapper02  

@dec01  
@dec02  
def test():  
    print("hell oword")  

test() 

## 运行结果:
aaaa
1111
2222
bbbb
hell oword
cccc
3333

当有多个函数时,函数的装载顺序是从下到上,即离函数最近的装饰器最先进行函数的装载。

运行结果分析:在声明函数时,装饰器就会进行函数的装载。首先是装饰器dec02进行装载,装载时会执行外函数,即打印”aaaa“,此时test=dec02(test),dec02(test)会返回wrapper02,即test=wrapper02。然后是dec01进行装载,同dec02一样,装载时会执行外函数,即打印”1111“,此时test=dec01(wrapper02),dec01(wrapper02)会返回wrapper01,即test=wrapper01。所以,当调用test函数时,可以将test函数内部结构看成是这样

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值