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+VTK环境搭建及第一个简单程序代码
Dec 13 Python
Python实现发送与接收邮件的方法详解
Mar 28 Python
python 2.7.14安装图文教程
Apr 08 Python
用TensorFlow实现戴明回归算法的示例
May 02 Python
Python多进程池 multiprocessing Pool用法示例
Sep 07 Python
解决python 无法加载downsample模型的问题
Oct 25 Python
详解pandas库pd.read_excel操作读取excel文件参数整理与实例
Feb 17 Python
python实现两个文件夹的同步
Aug 29 Python
python图片指定区域替换img.paste函数的使用
Apr 09 Python
Python中Selenium库使用教程详解
Jul 23 Python
Python QT组件库qtwidgets的使用
Nov 02 Python
python spilt()分隔字符串的实现示例
May 21 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
用ODBC的分页显示
2006/10/09 PHP
discuz的php防止sql注入函数
2011/01/17 PHP
php图片缩放实现方法
2014/02/20 PHP
PHP+Mysql基于事务处理实现转账功能的方法
2015/07/08 PHP
PHP实现通过URL提取根域名
2016/03/31 PHP
基于CI(CodeIgniter)框架实现购物车功能的方法
2018/04/09 PHP
jquery autocomplete自动完成插件的的使用方法
2010/08/07 Javascript
JavaScript 5 新增 Array 方法实现介绍
2012/02/06 Javascript
jquery获取div宽度的实现思路与代码
2013/01/13 Javascript
下拉菜单点击实现连接跳转功能的js代码
2013/05/19 Javascript
jquery选择器之属性过滤选择器详解
2014/01/27 Javascript
利用Jquery实现可多选的下拉框
2014/02/21 Javascript
教你如何使用PHP输出中文JSON字符串
2014/05/22 Javascript
Bootstrap整体框架之CSS12栅格系统
2016/12/15 Javascript
详解微信小程序 通过控制CSS实现view隐藏与显示
2017/05/24 Javascript
微信小程序选择图片和放大预览图片功能
2017/11/02 Javascript
Bootstrap 模态框多次显示后台提交多次BUG的解决方法
2017/12/26 Javascript
使用vue-cli导入Element UI组件的方法
2018/05/16 Javascript
微信小程序实现自上而下字幕滚动
2018/07/14 Javascript
解决Nuxt使用axios跨域问题
2020/07/06 Javascript
vue中实现拖动调整左右两侧div的宽度的示例代码
2020/07/22 Javascript
js+for循环实现字符串自动转义的代码(把后面的字符替换前面的字符)
2020/12/24 Javascript
vue动态设置路由权限的主要思路
2021/01/13 Vue.js
Python编程语言的35个与众不同之处(语言特征和使用技巧)
2014/07/07 Python
python django 实现验证码的功能实例代码
2017/05/18 Python
python深度优先搜索和广度优先搜索
2018/02/07 Python
利用python实现短信和电话提醒功能的例子
2019/08/08 Python
使用Python获取当前工作目录和执行命令的位置
2020/03/09 Python
python实现小程序推送页面收录脚本
2020/04/20 Python
python中re模块知识点总结
2021/01/17 Python
幼儿教师考核制度
2014/01/25 职场文书
行政助理工作职责范本
2014/03/04 职场文书
服务承诺书格式
2014/05/21 职场文书
党的群众路线教育实践活动学习计划
2014/11/03 职场文书
垂直极限观后感
2015/06/08 职场文书
教你用Python matplotlib库制作简单的动画
2021/06/11 Python