Python-装饰器

  • 什么是装饰器?
    本质就是个函数,功能为其他函数添加附加功能

  • 装饰器两个原则,不能动原来的函数
    1)不修改被修饰函数的源代码
    2)不修改被修饰函数的调用方式

  • 知识储备
    装饰器=高阶函数+函数嵌套+闭包

  • 高阶函数
    满足下列条件之一便可称为是高阶函数:
    1)函数接受的参数是一个函数名;
    可以实现为函数增加功能

 #高阶函数
def foo():
     print('你好呀,letty')

def test(fun):
    start_time=time.time()
    fun()
    stop_time=time.time()
    print('函数的运行时间是%s' %(stop_time-start_time))
    
test(foo)   #增加了foo()的函数功能可以计时,但是改变了调用方式,一般的调用方式应该是foo()

结果:
你好呀,letty
函数的运行时间是0.0

2)函数的返回值是一个函数名
可以实现不改变函数的调用方式

def foo():
     print('你好呀,letty')
def test1(fun):
    return  fun

foo=test1(foo)
foo()   #这里foo仅是上边的变量名字

结果:
你好呀,letty

单高阶函数无法满足装饰器

  • 函数嵌套
    在函数内部定义函数
#套一层
def father(name):
    print('from father %s' %name)
    def son():...
    print(locals())  #打印当前层的局部变量
    
father('alex')

结果:
from father alex
{'son': <function father.<locals>.son at 0x000002A44FB53268>, 'name': 'alex'}

上边这个结果说明son()是局部变量,那就只能在内部调用,不能在外部调用

def father(name):
    print('from father %s' %name)
    def son():
        print('My father is %s' %name)
    print(locals())  #打印当前层的局部变量
    son()
结果:

from father alex
{'son': <function father.<locals>.son at 0x000002A44FB339D8>, 'name': 'alex'}
My father is alex

可以再多层嵌套

def father(name):
    print('from father %s' %name)
    def son():
        print('My father is %s' %name)
        def grandson():
           myname='lll'  
            print('My grandfather is %s' %name)
        grandson()
    print(locals())  #打印当前层的局部变量
    son()
    
father('alex')

结果:
from father alex
{'son': <function father.<locals>.son at 0x000002A44FB56268>, 'name': 'alex'}
My father is alex
My grandfather is alex

  • 闭包
    函数的嵌套
    包:一层一个包
    闭:封装,封装什么:封装变量
  • 装饰器基本实现:

装饰器的框架

def timer(fun):
    def wrapper():
        print(fun)
        fun()
    return wrapper  

如何用:


def timer(fun):
    def wrapper():
        print(fun)
        fun()
    return wrapper  

    
def test111():
    time.sleep(3)
    print('test111函数运行完毕')
    
wrapper=timer(test111)
wrapper()

结果:
<function test111 at 0x000002A44FB56598>
test111函数运行完毕

给函数加计时功能

def timer(fun):
    def wrapper():
        star_time=time.time()
        fun()
        stop_time=time.time()
        print('运行时间是%s' %(stop_time-star_time))
    return wrapper  

    
def test111():
    time.sleep(3)
    print('test111函数运行完毕')
    
test111=timer(test111)
test111()

结果:
test111函数运行完毕
运行时间是3.0148227214813232

上边代码就实现了增加新功能但是没有修改源码,没有改变函数调用方式,但是还有弊病:得赋值,把要增加功能的test111函数作为参数传过去

完善技巧
使用Python语法糖 : @

@timer  就相当于 test111=timer(test111)
def timer(fun):
    def wrapper():
        star_time=time.time()
        fun()
        stop_time=time.time()
        print('运行时间是%s' %(stop_time-star_time))
    return wrapper  

@timer        #修饰哪个函数,就在哪个函数加它
def test111():
    time.sleep(3)
    print('test111函数运行完毕')
    
test111()   #这时候就不是运行单独test111()函数了,而是加上装饰器之后的wrapper
结果:
test111函数运行完毕
运行时间是3.0118308067321777

函数闭包加上返回值
加到wrapper中

def timer(fun):
    def wrapper():
        star_time=time.time()
        res=fun()
        stop_time=time.time()
        print('运行时间是%s' %(stop_time-star_time))
        return res
    return wrapper
@timer   
def test111():
    time.sleep(3)
    print('test111函数运行完毕')
    return '123'
    
#test111=timer(test111)
res=test111()
print('test111加上返回值:%s' %res)

结果:
test111函数运行完毕
运行时间是3.000683069229126
test111加上返回值:123

函数闭包加上参数
被修饰的函数有参数时
把参数加到wrapper上,然后值会传给被修饰对象

def timer(fun):
    def wrapper(*args,**kwargs):   #这样就不用担心参数个数变化了,更灵活 ,可变长参数 *args将参数变成元组,**kwargs将参数变成字典
        star_time=time.time()
        res=fun(*args,**kwargs)  #上边传过来的
        stop_time=time.time()
        print('运行时间是%s' %(stop_time-star_time))
        return res
    return wrapper
@timer   
def test111(name,age,gender):
    time.sleep(3)
    print('test111函数运行完毕,名字是【%s】 年龄是【%s】 性别【%s】' %(name,age,gender))
    return '123'
   
res=test111('letty',22,'femal')
print('test111加上返回值:%s' %res)

结果:
test111函数运行完毕,名字是【letty】 年龄是【22】 性别【femal】
运行时间是3.000082015991211
test111加上返回值:123

为函数加上认证功能
后端就是一个个功能,功能用函数实现

#利用装饰器给函数加上认证功能
def auth_fun(fun):
    def wrapper(*args,**kwargs):
        username=input('用户名:').strip()
        passwd=input('密码:').strip()
        
        if username=='clt' and passwd=='123456':
            res=fun(*args,**kwargs)
            return res
            
        else:
            print('用户名或者密码错误!')
        
    return wrapper
        

@auth_fun
def index():
    print('欢迎来到网上商城')
@auth_fun
   
def home(name):
    print('欢迎回家 %s' %name)
 
index()
home('clt')

结果:

用户名:clt

密码:123456
欢迎来到网上商城

用户名:clt

密码:123456
欢迎回家 clt

装饰器模拟session
记录登录状态 ,使用全局变量

user_dic={'username':None,'login':False}
def auth_fun(fun):
    def wrapper(*args,**kwargs):
        if user_dic['username']  and  user_dic['login']:
            res=fun(*args,**kwargs)
            return res
        
        username=input('用户名:').strip()
        passwd=input('密码:').strip()
        
        if username=='clt' and passwd=='123456':
            user_dic['username']=username
            user_dic['login']=True
            res=fun(*args,**kwargs)
            return res
            
        else:
            print('用户名或者密码错误!')
        
    return wrapper
        

@auth_fun
def index():
    print('欢迎来到网上商城')
@auth_fun 
def home(name):
    print('欢迎回家 %s' %name)
 
index()
home('clt')

结果:只输一次用户名和密码就好了
用户名:clt

密码:123456
欢迎来到网上商城
欢迎回家 clt

上边的代码有个弊病:就是将用户名和密码写死了,下边完善

user_list=[
        {'name':'alex','passwd':'123456'},
        {'name':'clt','passwd':'123456'},
        {'name':'peiqi','passwd':'123456'},
        {'name':'qiaozhi','passwd':'123456'},
        ]
        

current_dic={'username':None,'login':False}
def auth_fun(fun):
    def wrapper(*args,**kwargs):
        if current_dic['username']  and  current_dic['login']:
            res=fun(*args,**kwargs)
            return res
        
        username=input('用户名:').strip()
        passwd=input('密码:').strip()
        for user_dic in user_list:
            if username==user_dic['name'] and passwd==user_dic['passwd']:
                current_dic['username']=username
                current_dic['login']=True
                res=fun(*args,**kwargs)
                return res
                
        else:
           print('用户名或者密码错误!')
        
    return wrapper
        

@auth_fun
def index():
    print('欢迎来到网上商城')
@auth_fun 
def home(name):
    print('欢迎回家 %s' %name)


print('befor-->',current_dic)
index()
print('after-->',current_dic)
home('clt')

结果:
befor--> {'username': None, 'login': False}

用户名:clt

密码:123456
欢迎来到网上商城
after--> {'username': 'clt', 'login': True}
欢迎回家 clt

#上边做用户认证,用户信息简单的来源于列表,而现实中可能有多种形式的来源方式,比如文件和数据库,并且数据库也有多种,比如关系型和非关系型。还有,每种函数的数据来源也可能不同,下边介绍带参数的装饰器

  • 带参数装饰器
    让用户选择认证类型

如何给写好的装饰器加参数?
闭包(前边只用了高阶函数和函数嵌套)
在原来的装饰器之外再加一层,最外边一层传到的参数,可以传进里边的所有层
然后改变调用方式

user_list=[
        {'name':'alex','passwd':'123456'},
        {'name':'clt','passwd':'123456'},
        {'name':'peiqi','passwd':'123456'},
        {'name':'qiaozhi','passwd':'123456'},
        ]
        

current_dic={'username':None,'login':False}

def auth(auth_type='fieldb'):
    
    def auth_fun(fun):
        def wrapper(*args,**kwargs):
            print('认证类型是%s' %auth_type)
            if auth_type=='field':
                if current_dic['username']  and  current_dic['login']:
                    res=fun(*args,**kwargs)
                    return res
                
                username=input('用户名:').strip()
                passwd=input('密码:').strip()
                for user_dic in user_list:
                    if username==user_dic['name'] and passwd==user_dic['passwd']:
                        current_dic['username']=username
                        current_dic['login']=True
                        res=fun(*args,**kwargs)
                        return res
                        
                else:
                   print('用户名或者密码错误!')
            elif auth_type=='idap':
                print('这个玩法厉害了!!')
                res=fun(*args,**kwargs)
                return res
            else:
                print('我不会玩!你自己玩吧!')
        return wrapper
    return auth_fun
            

@auth(auth_type='field')  #因为加括号了,所以这里直接运行,得到的是auth_fun的内存地址。实际上就等于 @auth_fun,但是附加了参数auth_type
def index():
    print('欢迎来到网上商城')
@auth(auth_type='idap')
def home(name):
    print('欢迎回家 %s' %name)
    
    
print('befor-->',current_dic)
index()
print('after-->',current_dic)
home('clt')

结果:
befor--> {'username': None, 'login': False}
认证类型是field

用户名:clt

密码:123456
欢迎来到网上商城
after--> {'username': 'clt', 'login': True}
认证类型是idap
这个玩法厉害了!!
欢迎回家 clt
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值