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 '' + fun() + ''

return inner

def makeItalic(fun):

print('----b----')

def inner():

print('----2----')

return '' + fun() + ''

return inner

@makeBold

@makeItalic

def test():

print('----c----')

print('----3----')

return 'hello python decorator'

ret = test()

print(ret)

输出结果:

----b----

----a----

----1----

----2----

----c----

----3----

hello python decorator

可以发现,先用第二个装饰器(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",所以在一层层调完之后,打印的结果为hello python decorator

对无参函数进行装饰

上面例子中的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()

输出结果为:

test init

func name is test

装饰器中的功能

this is test func

和之前的原理一样,当python解释器执行到到@Test时,会把当前test函数作为参数传入Test对象,调用init方法,同时将test函数指向创建的Test对象,那么在接下来执行test()的时候,其实就是直接对创建的对象进行调用,执行其call方法。

好了,到目前为止,基本把python装饰器及相关知识点讲完了,如有问题,欢迎指出哈~

以上所述是小编给大家介绍的python装饰器简介详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

python装饰器-python装饰器简介---这一篇也许就够了(推荐)相关推荐

  1. Python:闭包(简介、使用方法、nonlocal修改闭包内使用的外部变量)、装饰器(定义、作用、通用装饰器、多个装饰器、带参数的装饰器、类装饰器、装饰器方式添加WEB框架的路由)

    一.闭包的介绍 闭包可以保存函数内的变量 当闭包执行完毕,外部函数的变量才释放. # 闭包的作用:可以保存外部函数的变量 # 闭包的形成条件 # 1.函数嵌套 # 2.内部函数使用了外部函数的变量或者 ...

  2. python基础-函数之装饰器、迭代器与生成器

    1. 函数嵌套 1.1 函数嵌套调用 函数的嵌套调用:在调用一个函数的过程中,又调用了其他函数 def bar():print("from in the bar.")def foo ...

  3. python find函数_Python 装饰器填坑指南 | 最常见的报错信息、原因和解决方案

    本文为霍格沃兹测试学院学员学习笔记,进阶学习文末加群. Python 装饰器简介 装饰器(Decorator)是 Python 非常实用的一个语法糖功能.装饰器本质是一种返回值也是函数的函数,可以称之 ...

  4. Python 闭包、单个装饰器、多个装饰器、装饰器修饰类、应用场景

    1. 闭包 在 Python 中,函数也可以作为参数.我们可以执行下面的代码: def func(a, b):return a + bprint(func) 我们直接输出函数名,而没有加括号.输出结果 ...

  5. python重难点之装饰器详解

    背景 虽然之前看过装饰器的相关内容,但是今天想起来,一直没有好好总结一下,所以特地记录下关于装饰器的一系列用法. 要想理解装饰器首先要明确颇python中的三个概念: 1.一切函数皆为对象 2.高阶函 ...

  6. python描述器做权限控制_Python装饰器14-描述器

    描述器 这是Python一个重要的概念,英文名:Descriptor descriptor是对象的一个属性,只不过它存在于类的dict中并且有特殊方法get(可能还有set和__delete)而具有一 ...

  7. python三大神器之装饰器

    装饰器的形成过程 假如你要写一个计算函数执行时间的函数,代码如下: 1 import time 2 3 def func1(): 4 print('in func1') 5 6 def timer(f ...

  8. Python 进阶_闭包 装饰器

    目录 目录 闭包 函数的实质和属性 闭包有什么好处 小结 装饰器 更加深入的看看装饰器的执行过程 带参数的装饰器 装饰器的叠加 小结 装饰器能解决什么问题 小结 闭包 Closure: 如果内层函数引 ...

  9. python装饰器-Python——装饰器(Decorator)

    1.什么是装饰器? 装饰器放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上.和这个函数绑定在一起.在我们调用这个函数的时候,第一件事并不是执行这个函数,而是将这个函数做为参数传入它头顶 ...

最新文章

  1. 摘自一个读者读后rework的感受
  2. Java学习之 多态 Polymorphism
  3. swift 网络请求中含有特殊字符的解决方案
  4. Go Timer使用方法
  5. nginx-模块内存泄露调试
  6. 报名参加第103期设计论坛公益免费设计活动
  7. Git 操作笔记/pip换源
  8. supervisord安装使用简记
  9. spring boot controller 初始化_使用 Spring 快速创建 web 应用的两种方式
  10. 大咖博闻荟 | 基于NSX-T和AVI实现企业双活中心
  11. 怎么让计算机唱歌视频教程,如何制作快手唱歌视频
  12. bodymovin导出没有html文件,bodymovin导出动画json结果分析
  13. K3 CLOUD API接口说明书V2.0
  14. python实现文本翻译
  15. cad2019菜单栏怎么调出来_AutoCAD2019工具栏没了找不到解决方法 AutoCAD2019工具栏怎么调出来...
  16. 3DES解密之网页解密(xhr断点及追栈)
  17. WIN10无限蓝屏重启解决办法
  18. ubuntu文献翻译软件:兰译
  19. 【阿里云仓库 可用 2022】IDEA MAVEN setings.xml 配置
  20. C51最全111条汇编指令合集,以及使用时的注意事项,超详细

热门文章

  1. andorid 开发笔记 -- 问题与解决
  2. ASP.Net 获取当前时间
  3. java内存区域之程序计数器
  4. 【SAP业务模式】之ICS(五):定价配置
  5. c#大圣之路笔记——c# SqlDataReader和SqlDataAdapter区别
  6. SmartWeatherAPI C#版
  7. APUE读书笔记-第14章-高级I/O
  8. CHOJ# 在线编译及评判系统(OnlineJudge)2.0 Beta2
  9. python字符串截取split-python实现字符串完美拆分split()的方法
  10. python话雷达图-Python 详解雷达图/蛛网图