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将mdb数据库文件导入postgresql数据库示例
Feb 17 Python
Python中字符串的处理技巧分享
Sep 17 Python
Python实现的特征提取操作示例
Dec 03 Python
Python 调用 zabbix api的方法示例
Jan 06 Python
python向字符串中添加元素的实例方法
Jun 28 Python
python实现爬取百度图片的方法示例
Jul 06 Python
django-filter和普通查询的例子
Aug 12 Python
python 实现方阵的对角线遍历示例
Nov 29 Python
Python如何将图像音视频等资源文件隐藏在代码中(小技巧)
Feb 16 Python
Python可以用来做什么
Nov 23 Python
Python抖音快手代码舞(字符舞)的实现方法
Feb 07 Python
Jupyter安装拓展nbextensions及解决官网下载慢的问题
Mar 03 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对大文件进行读取操作的实现代码
2013/01/23 PHP
php分页示例分享
2014/04/30 PHP
PHP自定义错误用法示例
2016/09/28 PHP
PHP之将POST数据转化为字符串的实现代码
2016/11/03 PHP
PHP+Redis链表解决高并发下商品超卖问题(实现原理及步骤)
2020/08/03 PHP
基于jquery的动态创建表格的插件
2011/04/05 Javascript
JS实现图片横向滚动效果示例代码
2013/09/04 Javascript
window.open 以post方式传递参数示例代码
2014/02/27 Javascript
使用原生js封装webapp滑动效果(惯性滑动、滑动回弹)
2014/05/06 Javascript
js读取csv文件并使用json显示出来
2015/01/09 Javascript
基于jQuery Circlr插件实现产品图片360度旋转
2015/09/20 Javascript
Javascript函数式编程简单介绍
2015/10/11 Javascript
jquery背景跟随鼠标滑动导航
2015/11/20 Javascript
ECharts仪表盘实例代码(附源码下载)
2016/02/18 Javascript
JS简单实现String转Date的方法
2016/03/02 Javascript
JQuery中attr属性和jQuery.data()学习笔记【必看】
2016/05/18 Javascript
js仿百度切换皮肤功能(html+css)
2016/07/10 Javascript
javascript汉字拼音互转的简单实例
2016/10/09 Javascript
jQuery实现的仿百度,仿谷歌搜索下拉框效果示例
2016/12/30 Javascript
深究AngularJS中$sce的使用
2017/06/12 Javascript
vue2中的keep-alive使用总结及注意事项
2017/12/21 Javascript
vscode 调试 node.js的方法步骤
2020/09/15 Javascript
[01:51]历届DOTA2国际邀请赛举办地回顾 TI9落地上海
2018/08/26 DOTA
常见的在Python中实现单例模式的三种方法
2015/04/08 Python
Python queue队列原理与应用案例分析
2019/09/27 Python
python seaborn heatmap可视化相关性矩阵实例
2020/06/03 Python
CSS3实现简易版的刮刮乐效果
2016/09/27 HTML / CSS
CSS3制作彩色进度条样式的代码示例分享
2016/06/23 HTML / CSS
Html5 语法与规则简要概述
2014/07/29 HTML / CSS
比较基础的php面试题及答案-编程题
2012/10/14 面试题
经典婚礼主持开场白
2014/03/13 职场文书
2014年乡镇民政工作总结
2014/12/02 职场文书
2015年人事工作总结范文
2015/04/09 职场文书
导游词之南迦巴瓦峰
2019/11/19 职场文书
nginx刷新页面出现404解决方案(亲测有效)
2022/03/18 Servers
悬疑名作《朋友游戏》动画无字ED宣传片 新角色公开
2022/04/13 日漫