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爬虫框架Scrapy安装使用步骤
Apr 01 Python
浅谈插入排序算法在Python程序中的实现及简单改进
May 04 Python
Win7下Python与Tensorflow-CPU版开发环境的安装与配置过程
Jan 04 Python
python实现一个简单的ping工具方法
Jan 31 Python
Python opencv实现人眼/人脸识别以及实时打码处理
Apr 29 Python
django框架模型层功能、组成与用法分析
Jul 30 Python
python实现图片插入文字
Nov 26 Python
Python turtle画图库&&画姓名实例
Jan 19 Python
Python流程控制常用工具详解
Feb 24 Python
Python基于numpy模块实现回归预测
May 14 Python
Python描述数据结构学习之哈夫曼树篇
Sep 07 Python
通过实例解析python and和or使用方法
Nov 14 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使用CURL获取302跳转后的地址实例
2014/05/04 PHP
thinkPHP5.0框架自动加载机制分析
2017/03/18 PHP
Laravel向公共模板赋值方法总结
2019/06/25 PHP
thinkphp框架无限级栏目的排序功能实现方法示例
2020/03/29 PHP
js获取TreeView控件选中节点的Text和Value值的方法
2012/11/24 Javascript
js实现杯子倒水问题自动求解程序
2013/03/25 Javascript
JS(JQuery)操作Array的相关方法介绍
2014/02/11 Javascript
Js的Array数组对象详解
2016/02/22 Javascript
原生js实现autocomplete插件
2016/04/14 Javascript
JS组件系列之MVVM组件构建自己的Vue组件
2017/04/28 Javascript
Vue 2中ref属性的使用方法及注意事项
2017/06/12 Javascript
深入理解React中何时使用箭头函数
2017/08/23 Javascript
浅谈vue项目打包优化策略
2018/09/29 Javascript
Vue 使用formData方式向后台发送数据的实现
2019/04/14 Javascript
小程序绑定用户方案优化小结
2019/05/15 Javascript
解决layer.open弹出框不能获取input框的值为空的问题
2019/09/10 Javascript
解决layui动态加载复选框无法选中的问题
2019/09/20 Javascript
vue.js实现图书管理功能
2019/09/24 Javascript
Vue 刷新当前路由的实现代码
2019/09/26 Javascript
使用 JavaScript 创建并下载文件(模拟点击)
2019/10/25 Javascript
vue quill editor 使用富文本添加上传音频功能
2020/01/14 Javascript
js校验开始时间和结束时间
2020/05/26 Javascript
VUE前端从后台请求过来的数据进行转换数据结构操作
2020/11/11 Javascript
[54:15]DOTA2-DPC中国联赛 正赛 DLG vs Dragon BO3 第二场2月1日
2021/03/11 DOTA
Python中join和split用法实例
2015/04/14 Python
Python fileinput模块使用实例
2015/05/28 Python
Python中的默认参数实例分析
2018/01/29 Python
基于python实现从尾到头打印链表
2019/11/02 Python
Python tkinter三种布局实例详解
2020/01/06 Python
如何利用python进行时间序列分析
2020/08/04 Python
手把手教你用Django执行原生SQL的方法
2021/02/18 Python
Crabtree & Evelyn欧盟:豪华洗浴、身体和护发
2021/03/09 全球购物
报社实习生自荐信
2014/01/24 职场文书
2016年会开场白台词
2015/06/01 职场文书
浅谈Java父子类加载顺序
2021/08/04 Java/Android
Win11怎么解除儿童账号限制?Win11解除微软儿童账号限制方法
2022/07/07 数码科技