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 面向对象 成员的访问约束
Dec 23 Python
python发送arp欺骗攻击代码分析
Jan 16 Python
Python的requests网络编程包使用教程
Jul 11 Python
解决seaborn在pycharm中绘图不出图的问题
May 24 Python
Python函数返回不定数量的值方法
Jan 22 Python
Python爬虫beautifulsoup4常用的解析方法总结
Feb 25 Python
python字典一键多值实例代码分享
Jun 14 Python
PYTHON EVAL的用法及注意事项解析
Sep 06 Python
Python Tkinter Entry和Text的添加与使用详解
Mar 04 Python
Python Django中间件使用原理及流程分析
Jun 13 Python
Django nginx配置实现过程详解
Sep 10 Python
python 实现体质指数BMI计算
May 26 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
星际争霸 Starcraft 编年史
2020/03/14 星际争霸
对于ThinkPHP框架早期版本的一个SQL注入漏洞详细分析
2014/07/04 PHP
LaravelS通过Swoole加速Laravel/Lumen详解
2018/03/02 PHP
JQuery中each()的使用方法说明
2010/08/19 Javascript
jquery给图片添加鼠标经过时的边框效果
2013/11/12 Javascript
让jQuery Mobile不显示讨厌loading界面的方法
2014/02/19 Javascript
Area 区域实现post提交数据的js写法
2014/04/22 Javascript
通用javascript代码判断版本号是否在版本范围之间
2015/11/29 Javascript
JS设置cookie、读取cookie
2016/02/24 Javascript
原生javascript实现addClass,removeClass,hasClass函数
2016/02/25 Javascript
JavaScript简介_动力节点Java学院整理
2017/06/26 Javascript
图文介绍Vue父组件向子组件传值
2018/02/17 Javascript
详解JS中统计函数执行次数与执行时间
2018/09/04 Javascript
vue2.x集成百度UEditor富文本编辑器的方法
2018/09/21 Javascript
仿vue-cli搭建属于自己的脚手架的方法步骤
2019/04/17 Javascript
vue项目配置使用flow类型检查的步骤
2020/03/18 Javascript
深入webpack打包原理及loader和plugin的实现
2020/05/06 Javascript
[01:00:52]2018DOTA2亚洲邀请赛 4.4 淘汰赛 EG vs LGD 第一场
2018/04/05 DOTA
[08:54]DOTA2-DPC中国联赛 正赛 Aster vs LBZS 选手采访
2021/03/11 DOTA
使用Python编写类UNIX系统的命令行工具的教程
2015/04/15 Python
Python内存管理实例分析
2019/07/10 Python
python已协程方式处理任务实现过程
2019/12/27 Python
keras tensorflow 实现在python下多进程运行
2020/02/06 Python
Django ModelForm操作及验证方式
2020/03/30 Python
Python必须了解的35个关键词
2020/07/16 Python
Python Unittest原理及基本使用方法
2020/11/06 Python
美国益智玩具购物网站:Fat Brain Toys
2017/11/03 全球购物
世界上最大的在线汽车租赁预订平台:Rentalcars.com(支持中文)
2018/10/12 全球购物
智能电子秤、手表和健康监测仪:Withings(之前为诺基亚健康)
2018/10/30 全球购物
Groupon西班牙官方网站:在线优惠券和交易,节省高达70%
2021/03/13 全球购物
创业计划书中要认真思考的问题
2013/12/28 职场文书
药品促销活动方案
2014/02/14 职场文书
四风问题个人自查剖析材料思想汇报
2014/09/21 职场文书
党性分析自查总结
2014/10/14 职场文书
四十年同学聚会致辞
2015/07/28 职场文书
售房协议书范本
2015/08/11 职场文书