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实现在pickling的时候压缩的方法
Sep 25 Python
git使用.gitignore设置不生效或不起作用问题的解决方法
Jun 01 Python
Python实现求笛卡尔乘积的方法
Sep 16 Python
Python unittest模块用法实例分析
May 25 Python
Python格式化日期时间操作示例
Jun 28 Python
python 定时器,实现每天凌晨3点执行的方法
Feb 20 Python
Python基础学习之类与实例基本用法与注意事项详解
Jun 17 Python
Scrapy-Redis之RedisSpider与RedisCrawlSpider详解
Nov 18 Python
matplotlib 画动态图以及plt.ion()和plt.ioff()的使用详解
Jan 05 Python
jupyter notebook更换皮肤主题的实现
Jan 07 Python
Pytorch中Softmax与LogSigmoid的对比分析
Jun 05 Python
Python图像处理库PIL详细使用说明
Apr 06 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
PHP函数import_request_variables()用法分析
2016/04/02 PHP
Javascript的并行运算实现代码
2010/11/19 Javascript
JS 进度条效果实现代码整理
2011/05/21 Javascript
jquery blockUI 遮罩不能消失与不能提交的解决方法
2011/09/17 Javascript
jquery随机展示头像代码
2011/12/21 Javascript
关于eval 与new Function 到底该选哪个?
2013/04/17 Javascript
js同比例缩放图片的小例子
2013/10/30 Javascript
JavaScript DSL 流畅接口(使用链式调用)实例
2015/03/15 Javascript
javascript中callee与caller的区别分析
2015/04/20 Javascript
ClearTimeout消除闪动实例代码
2016/02/29 Javascript
js中动态创建json,动态为json添加属性、属性值的实例
2016/12/02 Javascript
微信小程序 swiper制作tab切换实现附源码
2017/01/21 Javascript
js实现九宫格的随机颜色跳转
2017/02/19 Javascript
67 个节约开发时间的前端开发者的工具、库和资源
2017/09/12 Javascript
微信小程序 数据绑定及运算的简单实例
2017/09/20 Javascript
搭建一个Koa后端项目脚手架的方法步骤
2019/05/30 Javascript
JS中this的4种绑定规则详解
2020/02/04 Javascript
javascript 函数的暂停和恢复实例详解
2020/04/25 Javascript
Vue是怎么渲染template内的标签内容的
2020/06/05 Javascript
vue-openlayers实现地图坐标弹框效果
2020/09/24 Javascript
[01:29:31]VP VS VG Supermajor小组赛胜者组第二轮 BO3第一场 6.2
2018/06/03 DOTA
Python脚本简单实现打开默认浏览器登录人人和打开QQ的方法
2016/04/12 Python
Python中线程的MQ消息队列实现以及消息队列的优点解析
2016/06/29 Python
Python PyQt5 Pycharm 环境搭建及配置详解(图文教程)
2019/07/16 Python
Django QuerySet查询集原理及代码实例
2020/06/13 Python
Linux内核产生并发的原因
2012/07/13 面试题
日语翻译个人求职的自我评价
2013/10/14 职场文书
读书演讲主持词
2014/03/18 职场文书
鸿星尔克广告词
2014/03/21 职场文书
2014最新党员批评与自我批评材料
2014/09/24 职场文书
合法的离婚协议书范本
2014/10/23 职场文书
2014年领导班子工作总结
2014/12/11 职场文书
演讲开场白台词大全
2015/05/29 职场文书
你有一份《诚信考试承诺书》待领取
2019/11/13 职场文书
使用PDF.js渲染canvas实现预览pdf的效果示例
2021/04/17 Javascript
MySQL创建高性能索引的全步骤
2021/05/02 MySQL