python装饰器语法与应用(python装饰器简介---这一篇也许就够了推荐)
python装饰器语法与应用
python装饰器简介---这一篇也许就够了推荐Python装饰器(decorator)是在程序开发中经常使用到的功能,合理使用装饰器,能让我们的程序如虎添翼。
装饰器引入
初期及问题诞生
假如现在在一个公司,有A B C三个业务部门,还有S一个基础服务部门,目前呢,S部门提供了两个函数,供其他部门调用,函数如下:
|
def f1(): print ( 'f1 called' ) def f2(): print ( 'f2 called' ) |
在初期,其他部门这样调用是没有问题的,随着公司业务的发展,现在S部门需要对函数调用假如权限验证,如果有权限的话,才能进行调用,否则调用失败。考虑一下,如果是我们,该怎么做呢?
方案集合
- 让调用方也就是ABC部门在调用的时候,先主动进行权限验证
- S部门在对外提供的函数中,首先进行权限认证,然后再进行真正的函数操作
问题
- 方案一,将本不该暴露给外层的权限认证,暴露在使用方面前,同时如果有多个部门呢,要每个部门每个人都要周知到,你还不缺定别人一定会这么做,不靠谱。。。
- 方案二,看似看行,可是当S部门对外提供更多的需要进行权限验证方法时,每个函数都要调用权限验证,同样也实在费劲,不利于代码的维护性和扩展性
那么,有没有一种方法能够遵循代码的开放闭合原则,来完美的解决此问题呢?
装饰器引入
答案肯定是有的,不然真的是弱爆了。先看代码
|
def w1(func): def inner(): print ( '...验证权限...' ) func() return inner @w1 def f1(): print ( 'f1 called' ) @w1 def f2(): print ( 'f2 called' ) f1() f2() |
输出结果为
...验证权限...
f1 called
...验证权限...
f2 called
可以通过代码及输出看到,在调用f1 f2 函数时,成功进行了权限验证,那么是怎么做到的呢?其实这里就使用到了装饰器,通过定义一个闭包函数w1,在我们调用函数上通过关键词@w1,这样就对f1 f2函数完成了装饰。
装饰器原理
首先,开看我们的装饰器函数w1,该函数接收一个参数func,其实就是接收一个方法名,w1内部又定义一个函数inner,在inner函数中增加权限校验,并在验证完权限后调用传进来的参数func,同时w1的返回值为内部函数inner,其实就是一个闭包函数。
然后,再来看一下,在f1上增加@w1,那这是什么意思呢?当python解释器执行到这句话的时候,会去调用w1函数,同时将被装饰的函数名作为参数传入(此时为f1),根据闭包一文分析,在执行w1函数的时候,此时直接把inner函数返回了,同时把它赋值给f1,此时的f1已经不是未加装饰时的f1了,而是指向了w1.inner函数地址。
接下来,在调用f1()的时候,其实调用的是w1.inner函数,那么此时就会先执行权限验证,然后再调用原来的f1(),该处的f1就是通过装饰传进来的参数f1。
这样下来,就完成了对f1的装饰,实现了权限验证。
装饰器知识点
执行时机
了解了装饰器的原理后,那么它的执行时机是什么样呢,接下来就来看一下。
国际惯例,先上代码
|
def w1(fun): print ( '...装饰器开始装饰...' ) def inner(): print ( '...验证权限...' ) fun() return inner @w1 def test(): print ( 'test' ) test() |
输出结果为
...装饰器开始装饰...
...验证权限...
test
由此可以发现,当python解释器执行到@w1时,就开始进行装饰了,相当于执行了如下代码:
|
test = w1(test) |
两个装饰器执行流程和装饰结果
当有两个或两个以上装饰器装饰一个函数时,那么执行流程和装饰结果是什么样的呢?同样,还是以代码来说明问题。
|
def makeBold(fun): print ( '----a----' ) def inner(): print ( '----1----' ) return '<b>' + fun() + '</b>' return inner def makeItalic(fun): print ( '----b----' ) def inner(): print ( '----2----' ) return '<i>' + fun() + '</i>' return inner @makeBold @makeItalic def test(): print ( '----c----' ) print ( '----3----' ) return 'hello python decorator' ret = test() print (ret) |
输出结果:
----b----
----a----
----1----
----2----
----c----
----3----
<b><i>hello python decorator</i></b>
可以发现,先用第二个装饰器(makeItalic)进行装饰,接着再用第一个装饰器(makeBold)进行装饰,而在调用过程中,先执行第一个装饰器(makeBold),接着再执行第二个装饰器(makeItalic)。
为什么呢,分两步来分析一下。
- 装饰时机 通过上面装饰时机的介绍,我们可以知道,在执行到@makeBold的时候,需要对下面的函数进行装饰,此时解释器继续往下走,发现并不是一个函数名,而又是一个装饰器,这时候,@makeBold装饰器暂停执行,而接着执行接下来的装饰器@makeItalic,接着把test函数名传入装饰器函数,从而打印'b',在makeItalic装饰完后,此时的test指向makeItalic的inner函数地址,这时候有返回来执行@makeBold,接着把新test传入makeBold装饰器函数中,因此打印了'a'。
- 在调用test函数的时候,根据上述分析,此时test指向makeBold.inner函数,因此会先打印‘1‘,接下来,在调用fun()的时候,其实是调用的makeItalic.inner()函数,所以打印‘2‘,在makeItalic.inner中,调用的fun其实才是我们最原声的test函数,所以打印原test函数中的‘c‘,‘3‘,所以在一层层调完之后,打印的结果为<b><i>hello python decorator</i></b> 。
对无参函数进行装饰
上面例子中的f1 f2都是对无参函数的装饰,不再单独举例
对有参函数进行装饰
在使用中,有的函数可能会带有参数,那么这种如何处理呢?
代码优先:
|
def w_say(fun): """ 如果原函数有参数,那闭包函数必须保持参数个数一致,并且将参数传递给原方法 """ def inner(name): """ 如果被装饰的函数有行参,那么闭包函数必须有参数 :param name: :return: """ print ( 'say inner called' ) fun(name) return inner @w_say def hello(name): print ( 'hello ' + name) hello( 'wangcai' ) |
输出结果为:
say inner called
hello wangcai
具体说明代码注释已经有了,就不再单独说明了。
此时,也许你就会问了,那是一个参数的,如果多个或者不定长参数呢,该如何处理呢?看看下面的代码你就秒懂了。
|
def w_add(func): def inner( * args, * * kwargs): print ( 'add inner called' ) func( * args, * * kwargs) return inner @w_add def add(a, b): print ( '%d + %d = %d' % (a, b, a + b)) @w_add def add2(a, b, c): print ( '%d + %d + %d = %d' % (a, b, c, a + b + c)) add( 2 , 4 ) add2( 2 , 4 , 6 ) |
输出结果为:
add inner called
2 + 4 = 6
add inner called
2 + 4 + 6 = 12
利用python的可变参数轻松实现装饰带参数的函数。
对带返回值的函数进行装饰
下面对有返回值的函数进行装饰,按照之前的写法,代码是这样的
|
def w_test(func): def inner(): print ( 'w_test inner called start' ) func() print ( 'w_test inner called end' ) return inner @w_test def test(): print ( 'this is test fun' ) return 'hello' ret = test() print ( 'ret value is %s' % ret) |
输出结果为:
w_test inner called start
this is test fun
w_test inner called end
ret value is None
可以发现,此时,并没有输出test函数的‘hello',而是None,那是为什么呢,可以发现,在inner函数中对test进行了调用,但是没有接受不了返回值,也没有进行返回,那么默认就是None了,知道了原因,那么来修改一下代码:
|
def w_test(func): def inner(): print ( 'w_test inner called start' ) str = func() print ( 'w_test inner called end' ) return str return inner @w_test def test(): print ( 'this is test fun' ) return 'hello' ret = test() print ( 'ret value is %s' % ret) |
输出结果:
w_test inner called start
this is test fun
w_test inner called end
ret value is hello
这样就达到预期,完成对带返回值参数的函数进行装饰。
带参数的装饰器
介绍了对带参数的函数和有返回值的函数进行装饰,那么有没有带参数的装饰器呢,如果有的话,又有什么用呢?
答案肯定是有的,接下来通过代码来看一下吧。
|
def func_args(pre = 'xiaoqiang' ): def w_test_log(func): def inner(): print ( '...记录日志...visitor is %s' % pre) func() return inner return w_test_log # 带有参数的装饰器能够起到在运行时,有不同的功能 # 先执行func_args('wangcai'),返回w_test_log函数的引用 # @w_test_log # 使用@w_test_log对test_log进行装饰 @func_args ( 'wangcai' ) def test_log(): print ( 'this is test log' ) test_log() |
输出结果为:
...记录日志...visitor is wangcai
this is test log
简单理解,带参数的装饰器就是在原闭包的基础上又加了一层闭包,通过外层函数func_args的返回值w_test_log就看出来了,具体执行流程在注释里已经说明了。
好处就是可以在运行时,针对不同的参数做不同的应用功能处理。
通用装饰器
介绍了这么多,在实际应用中,如果针对没个类别的函数都要写一个装饰器的话,估计就累死了,那么有没有通用万能装饰器呢,答案肯定是有的,废话不多说,直接上代码。
|
def w_test(func): def inner( * args, * * kwargs): ret = func( * args, * * kwargs) return ret return inner @w_test def test(): print ( 'test called' ) @w_test def test1(): print ( 'test1 called' ) return 'python' @w_test def test2(a): print ( 'test2 called and value is %d ' % a) test() test1() test2( 9 ) |
输出结果为:
test called
test1 called
test2 called and value is 9
把上面几种示例结合起来,就完成了通用装饰器的功能,原理都同上,就不过多废话了。
类装饰器
装饰器函数其实是一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。
在python中,一般callable对象都是函数,但是也有例外。比如只要某个对象重写了call方法,那么这个对象就是callable的。
当创建一个对象后,直接去执行这个对象,那么是会抛出异常的,因为他不是callable,无法直接执行,但进行修改后,就可以直接执行调用了,如下
|
class Test( object ): def __call__( self , * args, * * kwargs): print ( 'call called' ) t = Test() print (t()) |
输出为:
call called
下面,引入正题,看一下如何用类装饰函数。
|
class Test( object ): def __init__( self , func): print ( 'test init' ) print ( 'func name is %s ' % func.__name__) self .__func = func def __call__( self , * args, * * kwargs): print ( '装饰器中的功能' ) self .__func() @Test def test(): print ( 'this is test func' ) test() |
输出结果为:
您可能感兴趣
- python虚拟环境和包使用教程(在win10和linux上分别安装Python虚拟环境的方法步骤)
- python浮点型和整数型(实例讲解Python中浮点型的基本内容)
- python socket 库(Pythony运维入门之Socket网络编程详解)
- python编写计算机病毒教程(Python告诉你木马程序的键盘记录原理)
- python中比较同一字典value(在Python 字典中一键对应多个值的实例)
- python有哪几种赋值(关于python多重赋值的小问题)
- python如何读取文件(Python从文件中读取数据的方法讲解)
- python注册码实现(python实现Virginia无密钥解密)
- python编程ide工具(这6款Python IDE&代码编辑器,你都用过吗?)
- python3.7保存文件(详解用python实现基本的学生管理系统文件存储版python3)
- python将对象转换成json(python对象与json相互转换的方法)
- python排列组合计算方法(Python实现的排列组合、破解密码算法示例)
- python爬网验证码在哪里(详解python 爬取12306验证码)
- python类定义(浅谈python新式类和旧式类区别)
- python怎么测试api接口(python接口自动化测试之接口数据依赖的实现方法)
- pythonweb大数据分析(Python实现的大数据分析操作系统日志功能示例)
- 三千年第一美女鞠婧祎出演白素贞pk杨紫,颜值胜赵雅芝王祖贤(三千年第一美女鞠婧祎出演白素贞pk杨紫)
- 成都轨道交通13号线一期工程最新进展(成都轨道交通13号线一期工程最新进展)
- 越南没事(越南没事情做)
- 重庆旅游攻略(重庆旅游3-4天攻略最佳线路)
- 周杰伦演唱会门票(周杰伦演唱会门票多少钱一张2023)
- 焕然一新 成都轨道集团官方网站改版上线(成都轨道集团官方网站改版上线)
热门推荐
- apache服务器的主配置(Apache中.htaccess文件功能)
- python中pip和pip3有什么区别(ISAPI-REWRITE伪静态规则写法以及说明)
- docker 运行容器命令详细参数详解(Docker Hub运行原理及实现过程解析)
- 如何采集nginx的日志(Filebeat 采集 Nginx 日志的方法)
- MongoDB 日志文件太大
- mysql8.0.15安装详细教程(mysql8.0.11数据目录迁移的实现)
- 织梦dedecms的时间调用标签(dedecms/织梦专题节点列表内容实现分页的方法介绍)
- http状态码304
- oracle中decode函数
- dockerfile镜像介绍(使用Dockerfile构建docker镜像)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9