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实现抓取页面上链接的简单爬虫分享
Jan 21 Python
python如何对实例属性进行类型检查
Mar 20 Python
Python实现的端口扫描功能示例
Apr 08 Python
widows下安装pycurl并利用pycurl请求https地址的方法
Oct 15 Python
Python实现微信自动好友验证,自动回复,发送群聊链接方法
Feb 21 Python
Python中dict和set的用法讲解
Mar 28 Python
django ManyToManyField多对多关系的实例详解
Aug 09 Python
Python笔记之工厂模式
Nov 20 Python
浅谈Pytorch torch.optim优化器个性化的使用
Feb 20 Python
python实现密码强度校验
Mar 18 Python
Python的collections模块真的很好用
Mar 01 Python
Python中如何处理常见报错
Jan 18 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中通过虚代理实现延迟加载的实现代码
2011/06/10 PHP
Yii2框架实现数据库常用操作总结
2017/02/08 PHP
jquery构造器的实现代码小结
2011/05/16 Javascript
js猜数字小游戏的简单实现代码
2013/07/02 Javascript
查看大图功能代码jquery版
2013/11/05 Javascript
JQuery以JSON方式提交数据到服务端示例代码
2014/05/05 Javascript
JS合并数组的几种方法及优劣比较
2014/09/19 Javascript
js中iframe调用父页面的方法
2014/10/30 Javascript
JavaScript组合拼接字符串的效率对比测试
2014/11/06 Javascript
js实现进度条的方法
2015/02/13 Javascript
JavaScript获得表单target属性的方法
2015/04/02 Javascript
10个很棒的jQuery代码片段
2015/09/24 Javascript
jquery UI Datepicker时间控件的使用及问题解决
2016/04/28 Javascript
深入理解jQuery.data() 的实现方式
2016/11/30 Javascript
AngularJS实现用户登录状态判断的方法(Model添加拦截过滤器,路由增加限制)
2016/12/12 Javascript
关于预加载InstantClick的问题解决方法
2017/09/12 Javascript
React操作真实DOM实现动态吸底部的示例
2017/10/23 Javascript
使用axios实现上传图片进度条功能
2017/12/21 Javascript
vue自定义指令directive实例详解
2018/01/17 Javascript
vue组件实现进度条效果
2018/06/06 Javascript
详解CommonJS和ES6模块循环加载处理的区别
2018/12/26 Javascript
基于Vue实现电商SKU组合算法问题
2019/05/29 Javascript
vue mvvm数据响应实现
2020/11/11 Javascript
[01:32]完美世界DOTA2联赛10月29日精彩集锦
2020/10/30 DOTA
python3模拟百度登录并实现百度贴吧签到示例分享(百度贴吧自动签到)
2014/02/24 Python
在Python中使用PIL模块处理图像的教程
2015/04/29 Python
numpy给array增加维度np.newaxis的实例
2018/11/01 Python
HTML5 video标签(播放器)学习笔记(一):使用入门
2015/04/24 HTML / CSS
奇怪的鱼:Weird Fish
2018/03/18 全球购物
英国户外玩具儿童游乐设备网站:TP Toys(蹦床、攀爬框架、秋千、滑梯和游戏屋)
2018/04/09 全球购物
《大海那边》教学反思
2014/04/09 职场文书
计算机多媒体专业自荐信
2014/07/04 职场文书
春节慰问信范文
2015/02/15 职场文书
2015年护士节慰问信
2015/03/23 职场文书
我对PyTorch dataloader里的shuffle=True的理解
2021/05/20 Python
详解python字符串驻留技术
2021/05/21 Python