首页 > 编程语言 >Python装饰器

Python装饰器

时间:2024-08-02 09:17:23浏览次数:14  
标签:Python def printdebug user print login 装饰 decorator

Python装饰器

Table of Contents

想理解Python的装饰器,首先要知道在Python中函数也是一个对象,所以可以:

  • 将函数赋值给变量
  • 将函数当做参数
  • 返回一个函数

引子

如果想在login函数中输出调试信息,可以这样做:

#!/usr/bin/python
# -*- coding:utf-8 -*-  
#Filename: print_debug.py

def login():
    print('in login')

def printdebug(func):
    print('enter the login')
    func()
    print('exit the login')

printdebug(login)

测试结果:

enter the login
in login
exit the login

缺点就是每次调用login都通过printdebug来调用

函数式调用

既然函数可以作为返回值,可以赋值给变量,那让代码优美一点:

#!/usr/bin/python
# -*- coding:utf-8 -*-  
#Filename: print_debug_with_function.py

def login():
    print('in login')

def printdebug(func): #func函数作为参数传入
    def __decorator():
        print('enter the login')
        func()
        print('exit the login')
    return __decorator  #函数作为返回值

debug_login = printdebug(login)  #将函数赋值给变量

debug_login()  #通过变量来调用的函数

这样每次只要调用debug_login就可以了。将原先的两个函数printdebug和login绑定到一起,成为debug_login。这种耦合叫内聚

语法糖

printdebug和login是通过

debug_login = printdebug(login)

来结合的,但这似乎也是多余的,我们希望在定义login上加个标注,从而将printdebug和login结合起来。Python的解决方案是提供一个语法糖,用一个@符号来结合它们:

def printdebug(func):
    def __decorator():
        print('enter the login')
        func()
        print('exit the login')
    return __decorator  

@printdebug  #把login做为func的实际参数传入printdebug的
def login():
    print('in login')

login()  #使得调用更加自然, 实际上是在调用printdebug(login)()

__decorator函数就是一个:使用函数作参数并且返回函数的函数。这样改进的好处是:

  • 更简短的代码,将结合点放在函数定义时
  • 不改变原函数的函数名

事实上在Python解释器发现login调用时,会将login转换为:

printdebug(login)()

__decorator ()

也就是说真正执行的是__decorator(把func绑定成实参login函数)这个函数

加上参数

 

login函数有参数

login函数可能有参数,比如login的时候传人user的信息。也就是说,要这样调用login:

login(user)

Python会将login的参数直接传给__decorator这个函数。因此可以直接在__decorator中使用user变量:

#!/usr/bin/python
# -*- coding:utf-8 -*-  
#Filename: login_with_param_decorator.py
def printdebug(func):
    def __decorator(user):    # 增加传递给login的参数
        print('enter the login')
        func(user)  # login调用带上参数
        print('exit the login')
    return __decorator  

@printdebug 
def login(user):
    print('in login:' + user)

login('jatsz')  # 真实调用是__decorator('jatsz') 

测试结果:

enter the login
in login:jatsz
exit the login

事实上的调用过程是:

login('jatsz')

printdebug(login)('jatsz')

__decorator('jatsz') 

装饰器本身有参数

在定义decorator时,也可以带入参数,比如这样使用decorator,传入一个参数来指定debug level

#!/usr/bin/python
# -*- coding:utf-8 -*-  
#Filename: decorator_with_param.py

def printdebug_level(level):  #通过wrapper来增加装饰器的参数
    def printdebug(func):
        def __decorator(user):    
            print('enter the login, and debug level is: ' + str(level)) #打印debug等级
            func(user)  
            print('exit the login')
        return __decorator  
    return printdebug    #返回原始的装饰器

@printdebug_level(level=5)   #传入装饰器的debug等级参数为5
def login(user):
    print('in login:' + user)

login('jatsz') #等价于printdebug_level(5) (login) ('jatsz') 

测试结果:

enter the login, and debug level is: 5
in login:jatsz
exit the login

此时的pringdebug函数相当于pringdebug_level(5)

装饰有返回值的函数

有时候login会有返回值,比如返回message来表明login是否成功:

login_result = login(‘jatsz’)

这时候需要将返回值在decorator和调用函数间传递:

#!/usr/bin/python
# -*- coding:utf-8 -*-  
#Filename: decorator_return_result.py

def printdebug(func):
    def __decorator(user):    
        print('enter the login')
        result = func(user)  
        print('exit the login')
        return result        #在装饰器函数返回调用func的结果
    return __decorator  

@printdebug 
def login(user):
    print('in login:' + user)
    msg = "success" if user == "jatsz" else "fail"
    return msg  # login函数返回结果

result1 = login('jatsz')
print(result1) #success 

result2 = login('candy')
print (result2) #fail

测试结果:

enter the login
in login:jatsz
exit the login
success

enter the login
in login:candy
exit the login
fail

多个装饰器

可以对一个函数应用多个装饰器,这时需要留心的是应用装饰器的顺序对结果会产生影响。例如:

#!/usr/bin/python
# -*- coding:utf-8 -*-  
#Filename: multiple_decorators.py

def printdebug(func):
    def __decorator():    
        print('enter the login')
        func() 
        print('exit the login')
    return __decorator  

def others(func):    
    def __decorator():
        print ('***other decorator***')
        func()
    return __decorator

@others         #相当于others(printdebug(login)) ()
@printdebug
def login():
    print('in login:')

@printdebug    #相当于printdebug(others(login)) ()
@others
def logout():
    print('in logout:')

login()
print('---------------------------') 
logout()

测试结果:

***other decorator***
enter the login
in login:
exit the login
---------------------------
enter the login
***other decorator***
in logout:
exit the login

login和logout输出截然相同。造成这个输出不同的原因是应用装饰器的顺序不同。回头看看login的定义,是先应用others,然后才是printdebug。而logout函数正好相反,在逻辑上可以将logout函数应用装饰器的过程这样看:

@printdebug    
(
    @others
    (
        def logout():
            print('in logout:')
    )
)

灵活运用

装饰器不能对函数的一部分应用,只能作用于整个函数。假如想对下面这行语句应用装饰器:

msg = "success" if user == "jatsz" else "fail"

那就需要对这行代码提取出一个函数,然后再对它应用修饰器:

#!/usr/bin/python
# -*- coding:utf-8 -*-  
#Filename: validator_decorator.py

def printdebug(func):
    def __decorator(user):    
        print('enter the login')
        result = func(user) 
        print('exit the login')
        return result      
    return __decorator  

def login(user):
    print('in login:' + user)
    msg = validate(user)  #抽取要应用修饰器的方法
    return msg  

@printdebug  #对validate函数应用修饰器
def validate(user):
    msg = "success" if user == "jatsz" else "fail"
    return msg

result1 = login('jatsz');
print (result1)

测试结果:

in login:jatsz
enter the login
exit the login
success

实际上validate往往是个耗时的过程。为了提高应用的性能,会将validate的结果cache一段时间(30 seconds),借助decorator和上面的方法,可以这样实现:

#!/usr/bin/python
# -*- coding:utf-8 -*-  
#Filename: cache_validator.py

import time

dictcache = {}

def cache(func):
    def __decorator(user):    
        now = time.time()
        if (user in dictcache):
            result,cache_time = dictcache[user]
            if (now - cache_time) > 30:  #cache expired
                result = func(user)
                dictcache[user] = (result, now)  #cache the result by user
            else:
                print('cache hits')
        else:
            result = func(user)
            dictcache[user] = (result, now)
        return result      
    return __decorator  

def login(user):
    print('in login:' + user)
    msg = validate(user)  
    return msg  

@cache  #apply the cache for this slow validation
def validate(user):
    time.sleep(5)  #simulate 10 second block
    msg = "success" if user == "jatsz" else "fail"
    return msg

result1 = login('jatsz'); print (result1)  
result2 = login('jatsz'); print (result2)    #this login will return immediately by hit the cache
result3 = login('candy'); print (result3)

测试结果:

in login:jatsz
success

in login:jatsz
cache hits
success

in login:candy
fail
 

标签:Python,def,printdebug,user,print,login,装饰,decorator
From: https://www.cnblogs.com/flyingsir/p/18337992

相关文章

  • Python数据容器(2)
    一、数据容器:tuple(元组)1.定义同列表一样,但是形成后不可修改单个元组需要加上单独的逗号2.特定可以容纳多个数据可以不同数据类型混装运行数据重复不可修改支持循环3.特例元组中如果有list列表,则可以修改list中的数据4.常用操作下标查询语法:元组.index(元素)统计个......
  • Python数据容器(1)
    一、数据容器入门1.定义一份变量多个数据一个数据称为1个元素2.特点是否支持重复元素是否可以修改是否有序3.类别列表(list)元组(tuple)字符串(str)集合(set)字典(dict)二、数据容器:list(列表)1.下标索引把列表元素取出来(左到右0→123)(右到左-1→-123)序号也可......
  • 初学Python:第五天
    今天学习了有关于字典的定义和相关操作:1、新增元素语法:字典[key]=value结果:字典被修改,新增了元素更新元素语法:字典[key]=value结果:字典被修改,元素被更新注意:字典key不可以重复,所以对已存在的key执行上述操作,就是更新value值2、删除元素语法:字典.pop(Key)结果:获得......
  • 在 Python 生成器中使用“with”语句管理资源
    今天,在编程时,我发现自己在生成器函数内管理资源(ssh连接),类似于以下内容:def_yield_fname(host_address,usr,pwd,datapath):withparamiko.SSHClient()asssh_client:ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())ssh_client.......
  • Avro:在 Python 中使用多处理进行解码
    就最佳性能和更少延迟而言,哪种异步方法更适合解码从Kafka主题消耗的Avro消息?我正在使用Avro库的并发future,并且我仍然可以看到类似的执行时间与不使用并发future的情况相比。fromavro.ioimportBinaryDecoder,DatumReaderfromconfluent_kafka.avro.cached_sc......
  • 白盒测试基础与实践:Python示例及流程图设计
    文章目录前言一、白盒测试是什么?主要特点常用方法优点缺点二、白盒测试常用技术语句覆盖判定覆盖条件覆盖判定/条件覆盖条件组合覆盖路径覆盖三、程序流程图设计四、测试用例设计1.基本路径法2.语句覆盖3.判断覆盖4.条件覆盖5.判断/条件覆盖6.条件组合覆盖总结......
  • 【python的语法特点,如注释规则、代码缩进、编写规范等】
    介绍一下python的语法特点,如注释规则、代码缩进、编写规范等Python是一种广泛使用的高级编程语言,以其简洁易读的语法、丰富的标准库和强大的第三方库而闻名。下面我将详细介绍Python的一些基本语法特点,包括注释规则、代码缩进、以及编写规范等。一、注释规则Python......
  • 深圳大学-数据科学导论实验-python数据探索
    实验目的与要求掌握python编程基础。掌握数据探索基本操作。实验环境WindowsPyCharm实验数据salaries.csv"","rank","discipline","yrs.since.phd","yrs.service","sex","salary""1","Prof","B",......
  • 基于Python+Django协同过滤算法的招聘信息推荐系统设计与实现(源码+数据库+讲解)
    文章目录前言详细视频演示项目运行截图技术框架后端采用Django框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • python 栈帧沙箱逃逸
    基础理论什么是生成器生成器是python中的一种特殊的迭代器,在每次生成值以后会保留当前状态,以便下次调用可以继续生成值.python中生成器通过yield关键词进行定义,每次调用的时候返回一个值,并保持当前状态的同时暂停函数的执行.当下一次调用生成器的时候,函数会从上次暂停的位......