Python 装饰器(decorator)常用的创建方式及解析


Posted in Python onApril 24, 2022

装饰器简介

装饰器(decorator)是一种高级Python语法。可以对一个函数、方法或者类进行加工。在Python中,我们有多种方法对函数和类进行加工,相对于其它方式,装饰器语法简单,代码可读性高。因此,装饰器在Python项目中有广泛的应用。修饰器经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理, Web权限校验, Cache等。

装饰器的优点是能够抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。即,可以将函数“修饰”为完全不同的行为,可以有效的将业务逻辑正交分解。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。例如记录日志,需要对某些函数进行记录。笨的办法,每个函数加入代码,如果代码变了,就悲催了。装饰器的办法,定义一个专门日志记录的装饰器,对需要的函数进行装饰。

基础通用装饰器

源码示例

def wrapper_out(func):
    print('-- wrapper_out start --')

    def inner(*args, **kwargs):
        print("-- inner start --")
        ret = func(*args, **kwargs)
        print("-- inner end --")
        return ret
    print('-- wrapper_out end --')
    return inner
@wrapper_out
def test():
    print("--test--")
    return 1 * 2
if __name__ == '__main__':
    print(">>>>>>>>>>>>>>")
    print(test())

执行结果

-- wrapper_out start --
-- wrapper_out end --
>>>>>>>>>>>>>>
-- inner start --
--test--
-- inner end --
2

带参数装饰器

源码示例

def wrapper_out(mode=None):
    print('-- wrapper_out start --')

    def inner_1(func):
        print("-- inner_1 start --")
        def inner_2(*args, **kwargs):
            print("-- inner_2 start --")
            print(f"mode: {mode}")
            ret = func(*args, **kwargs)
            print("-- inner_2 end --")
            return ret
        print("-- inner_2 end --")
        return inner_2
    print('-- wrapper_out end --')
    return inner_1
@wrapper_out(mode=2)
def test():
    print("--test--")
    return 1 * 2
if __name__ == '__main__':
    print(">>>>>>>>>>>>>>")
    print(test())

源码结果

-- wrapper_out start --
-- wrapper_out end --
-- inner_1 start --
-- inner_2 end --
>>>>>>>>>>>>>>
-- inner_2 start --
mode: 2
--test--
-- inner_2 end --
2

源码解析

带参数的装饰器函数, 需要多嵌套一层, 外层装饰器的参数

预加载的时候已经是根据函数的编写顺序进行加载

执行顺序在对应的最内存函数中调用最外层的装饰器函数参数

被装饰函数是最为 inner_1 的参数进行传入, 被装饰函数的参数是作为 inner_2 的参数传入

被装饰函数的执行位置是在 inner_2 中, 使用inner_1 的参数变量和 inner_2 的参数变量共同协助下进行执行

同时还要使用装饰器函数 wrapper_out 的参数变量进行额外的操作

多装饰器执行顺序

源码示例

def wrapper_out1(func):
    print('-- wrapper_out_1 start --')

    def inner1(*args, **kwargs):
        print("-- inner_1 start --")
        ret = func(*args, **kwargs)
        print("-- inner_1 end --")
        return ret
    print('-- wrapper_out1 end --')
    return inner1
def wrapper_out2(func):
    print('-- wrapper_out_2 start --')
    def inner2(*args, **kwargs):
        print("-- inner_2 start --")
        print("-- inner_2 end --")
    print('-- wrapper_out_2 end --')
    return inner2
@wrapper_out2
@wrapper_out1
def test():
    print("--test--")
    return 1 * 2
if __name__ == '__main__':
    print(">>>>>>>>>>>>>>")
    print(test())

执行结果

-- wrapper_out_1 start --
-- wrapper_out1 end --
-- wrapper_out_2 start --
-- wrapper_out_2 end --
>>>>>>>>>>>>>>
-- inner_2 start --
-- inner_1 start --
--test--
-- inner_1 end --
-- inner_2 end --
2

解析

装饰器的预加载顺序是从上往下, 先将装饰器函数写入内存

装饰器的执行顺序是以最靠近函数体的装饰器开始执行(从内到外)

类装饰器

源码示例

class WrapperOut(object):
    def __init__(self, func):
        print('start init ~~~~~`')
        print('func name is %s ' % func.__name__)
        self.__func = func
        print('end init ~~~~~`')

    def __call__(self, *args, **kwargs):
        print('start test')
        self.__func()
        print('end test')
@WrapperOut
def test():
    print('this is test func')
if __name__ == '__main__':
    print(">>>>>>>>>>>")
    test()

执行结果

start init ~~~~~`
func name is test 
end init ~~~~~`
>>>>>>>>>>>
start test
this is test func
end test

解析

类装饰器是利用了类初始化 init 析构方法来处理 被装饰函数的传入

以及使用 call 方法来满足被装饰函数的执行触发

到此这篇关于Python 装饰器常用的创建方式及解析的文章就介绍到这了!


Tags in this post...

Python 相关文章推荐
python中列表元素连接方法join用法实例
Apr 07 Python
Python 中 Meta Classes详解
Feb 13 Python
关于django 数据库迁移(migrate)应该知道的一些事
May 27 Python
python实现感知器算法(批处理)
Jan 18 Python
python字符串分割及字符串的一些常规方法
Jul 24 Python
详解Python time库的使用
Oct 10 Python
pytorch方法测试详解——归一化(BatchNorm2d)
Jan 15 Python
tensorflow 20:搭网络,导出模型,运行模型的实例
May 26 Python
python3中布局背景颜色代码分析
Dec 01 Python
如何用tempfile库创建python进程中的临时文件
Jan 28 Python
python中添加模块导入路径的方法
Feb 03 Python
python中的被动信息搜集
Apr 29 Python
解决IDEA翻译插件Translation报错更新TTK失败不能使用
python使用BeautifulSoup 解析HTML
Apr 24 #Python
Python中npy和mat文件的保存与读取
Apr 24 #Python
python小型的音频操作库mp3Play
Apr 24 #Python
5个pandas调用函数的方法让数据处理更加灵活自如
Apr 24 #Python
Python 使用 Frame tkraise() 方法在 Tkinter 应用程序中的Frame之间切换
Apr 24 #Python
在 Python 中利用 Pool 进行多线程
Apr 24 #Python
You might like
PHP4在Windows2000下的安装
2006/10/09 PHP
PHP中对数组的一些常用的增、删、插操作函数总结
2015/11/27 PHP
js调用后台servlet方法实例
2013/06/09 Javascript
jquery设置元素的readonly和disabled的写法
2013/09/22 Javascript
JavaScript极简入门教程(三):数组
2014/10/25 Javascript
JavaScript实现将xml转换成html table表格的方法
2015/04/17 Javascript
javascript动态设置样式style实例分析
2015/05/13 Javascript
node.js插件nodeclipse安装图文教程
2020/10/19 Javascript
使用jQuery实现鼠标点击左右按钮滑动切换
2017/08/04 jQuery
微信小程序 rich-text的使用方法
2017/08/04 Javascript
基于daterangepicker日历插件使用参数注意的问题
2017/08/10 Javascript
LayUi使用switch开关,动态的去控制它是否被启用的方法
2019/09/21 Javascript
vue cli4.0项目引入typescript的方法
2020/07/17 Javascript
nestjs返回给前端数据格式的封装实现
2021/02/22 Javascript
[02:08:58]2014 DOTA2国际邀请赛中国区预选赛 Ne VS CIS
2014/05/22 DOTA
用Python制作检测Linux运行信息的工具的教程
2015/04/01 Python
socket + select 完成伪并发操作的实例
2017/08/15 Python
python 删除大文件中的某一行(最有效率的方法)
2017/08/19 Python
Django内容增加富文本功能的实例
2017/10/17 Python
Django后台获取前端post上传的文件方法
2018/05/28 Python
python+selenium打印当前页面的titl和url方法
2018/06/22 Python
python数值基础知识浅析
2019/11/19 Python
python3通过qq邮箱发送邮件以及附件
2020/05/20 Python
html5与css3小应用
2013/04/03 HTML / CSS
浅析HTML5的WebSocket与服务器推送事件
2016/02/19 HTML / CSS
美国在线购买和出售礼品卡网站:EJ Gift Cards
2019/06/09 全球购物
NET程序员上机面试题
2015/05/23 面试题
军训自我鉴定
2013/12/14 职场文书
旺仔牛奶广告词
2014/03/20 职场文书
财务担保书范文
2014/04/02 职场文书
云南省召开党的群众路线教育实践活动总结会议新闻稿
2014/10/21 职场文书
2014年党总支工作总结
2014/12/18 职场文书
大四学生个人总结
2015/02/15 职场文书
小升初自荐信怎么写
2015/03/26 职场文书
Python 中的单分派泛函数你真的了解吗
2021/06/22 Python
学习nginx基础知识
2021/09/04 Servers