python装饰器的本质,就是闭包!

我们一般谈Python的闭包,都是指普通的入参,而谈装饰器的时候,入参一定有函数!闭包和装饰器,返回的都是函数。函数是代码的最小封装单位,装饰器作用于函数,它不影响函数自身的执行,只是在函数的执行前后增加一些“装饰性”的动作。装饰器被称为python的语法糖(syntax sugar),也被视为python支持AOP编程(面向切面编程)的工具。

简单装饰器

以下代码实现了一个最简单的额装饰器,功能是给带上装饰器的函数,在函数执行前,增加一行print打印,代码如下:

test1函数加上了calling_trace装饰器,在第7行,这一行代码的作用,相同于第11行的注释。test1还是test1,这个名字没有变,只是换成了一个闭包函数的返回值,此闭包函数,就是装饰器,它接收一个函数作为参数。以上代码运行效果如下:

$ python3 deco.py

calling test1

test1 is runing...

我们还可以稍微复杂一点点,既然是装饰器,被装饰的函数执行前后都可以加点小动作。修改后的代码如下:

def calling_trace(func):

def wrapper():

print('calling', func.__name__)

a = func()

print('this is the reture:',a)

return wrapper

@calling_trace

def test2():

print('test2 is runing...')

return 'www.pynote.net'

# test2 = calling_trace(test2)

test2()

新的装饰器没有改名字,只是获取了被装饰函数的返回值,并将返回值打印出来。运行效果如下:

$ python3 deco.py

calling test2

test2 is runing...

this is the reture: www.pynote.net

test2函数执行前后,都增加了一些动作,作为装饰!

这就是装饰器的概念:不修改已有函数的代码,也不修改已有函数的调用处代码,却达到了丰富函数功能的效果!本质上装饰器是对函数的一种封装,只是我们要理解@语法。

闭包和装饰器

装饰器的本质就是闭包,我们如果把上面这段代码重写一遍,采用闭包的语法形式,不使用@语法,效果是完全一样的:

def calling_trace(func):

def wrapper():

print('calling', func.__name__)

a = func()

print('this is the reture:',a)

return wrapper

def test2():

print('test2 is runing...')

return 'www.pynote.net'

t = calling_trace(test2)

t()

以上代码,没有@语法的使用啦,用变量t来获取calling_trace返回的函数对象,然后调用,效果与使用装饰器完全一样:

$ python3 deco.py

calling test2

test2 is runing...

this is the reture: www.pynote.net

使用@语法,阅读代码就如吃糖!

装饰带参数的函数

如果被装饰的函数有参数,需要在装饰器内部原样复制函数的参数定义。请看示例:

def calling_trace(func):

def wrapper(a,b,c=3):

print('calling', func.__name__)

a = func(a,b,c)

print('reture value:',a)

return wrapper

@calling_trace

def test3(a,b,c=3):

print('test3 is runing...')

return a+b+c

test3(1,2,5)

test3(1,2)

装饰器返回的函数的参数定义,要与被装饰函数的参数定义保持一致!以上代码运行结果如下:

$ python3 deco.py

calling test3

test3 is runing...

reture value: 8

calling test3

test3 is runing...

reture value: 6

就算装饰参数个数不定的函数,语法上也是一样的,请看下面代码,test4函数的参数个数不定:

def calling_trace(func):

def wrapper(*args):

print('calling', func.__name__)

a = func(*args)

print('reture value:',a)

return wrapper

@calling_trace

def test4(*args):

print('test4 is runing...')

return sum(args)

test4(1,2,3,4,5,6,7,8)

test4(23,34,45,56)

*args表示一个tuple,在函数定义处出现,就是packing打包调用时的参数,在调用时出现,就是unpacking展开tuple。跟**kw(对应dict)用法一样。以上代码运行效果:

$ python3 deco.py

calling test4

test4 is runing...

reture value: 36

calling test4

test4 is runing...

reture value: 158

给带参数的函数加装饰器,还有一种更通用更常见的参数写法,这种写法在修改函数参数和调用处时,有可以反过来保持装饰器部分代码不变。示例如下:

def calling_trace(func):

def wrapper(*args, **kw):

print('calling', func.__name__)

a = func(*args, **kw)

print('reture value:',a)

return wrapper

@calling_trace

def test5(a,b,c=3):

print('test5 is runing...')

return a+b+c

test5(1,2)

test5(1,2,c=8)

装饰器中使用*args和**kw来接收和传递参数!这个地方要好好体会,这是python的一个特别精妙的地方。以上代码运行结果如下:

$ python3 deco.py

calling test5

test5 is runing...

reture value: 6

calling test5

test5 is runing...

reture value: 11

带参数的装饰器

本文以上示例,都是不带参数的装饰器,在使用@语法的时候,没有参数。而装饰器本身也可以带参数,通过在装饰器外再使用闭包,给装饰器封装其执行环境,可以使装饰器的功能更强大更灵活,也可以更好的控制函数的执行(比如在某些情况下不执行)。而带参数的装饰器,在语法上,也就跟闭包区分开来。(应该就是这个原因,闭包和装饰器在python中是分成了两个概念)

def calling_trace(run):

def deco(func):

def wrapper(*args, **kw):

print('calling', func.__name__)

if run == 1:

a = func(*args, **kw)

print('reture value:',a)

else:

print('not allow to run')

return wrapper

return deco

# test5 = calling_trace(run=1)(test5)

@calling_trace(run=1)

def test5(a,b,c=3):

print('test5 is runing...')

return a+b+c

@calling_trace(run=0)

def test6(*args):

print('test6 is runing...')

return sum(args)

test5(1,2)

test5(1,2,c=8)

test6(23,34,45,56)

先看一下这段代码的运行结果:

$ python3 deco.py

calling test5

test5 is runing...

reture value: 6

calling test5

test5 is runing...

reture value: 11

calling test6

not allow to run

达到预期,test5可以执行,而test6不允许执行。

calling_trace实际上就是一个闭包,它有一个参数,run,调用calling_trace(run=1)后,就相当于返回了一个有run这个参数的装饰器。然后再用这个装饰器去“修饰”它自己的参数func。这个效果,就如注释掉的那行代码。如果不使用@语法,把那行代码注释打开(放在test5的定义后面),运行效果一模一样!

多重装饰器

函数可以有多个装饰器!多个装饰器的效果,就相当于对函数进行了多层的封装包裹,而不同的装饰器对函数执行的功能影响,完全独立。比如有个装饰器用来控制函数是否能够被执行,另一个装饰器控制函数的所有raise出来的异常。

@a

@b

@c

def tt(): pass

# tt = a(b(c(tt)))

tt函数上面带3个装饰器,想过正如注释掉的那行代码。

我真的觉得python装饰器的设计,实在是非常精妙!

-- EOF --

python重写和装饰器_python装饰器相关推荐

  1. python两层装饰器_python装饰器

    Python的装饰器的英文名叫Decorator,基本上适用的场景就是"装修":不涉及主流程业务,用于鉴权.审计等副业. 1.函数 在python中,函数通过def关键字.函数名和 ...

  2. python实现装饰器_python装饰器的实现

    说起装饰器我们可能已经很熟悉了(不了解的可以查看python基础学习--装饰器),随手就可以写一个简单的装饰器 def decorator(func): def inner(*args, **kwar ...

  3. python解读器_Python装饰器完全解读

    Python Python开发 Python语言 Python装饰器完全解读 1 引言 装饰器(Decorators)可能是Python中最难掌握的概念之一了,也是最具Pythonic特色的技巧,深入 ...

  4. python无参数装饰器_Python装饰器(不带参数)

    示例 直接给出示例,普通装饰器(即装饰器函数本身不带参数,或参数为实际被包裹的函数): import time from functools import wraps def timethis(fun ...

  5. python修饰器_python修饰器

    转自:http://www.cnblogs.com/rollenholt/archive/2012/05/02/2479833.html 文章先由stackoverflow上面的一个问题引起吧,如果使 ...

  6. python运行器_python 运行器

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! #usrbinenv python3 import timedef outer( ...

  7. python简单装饰器_python装饰器的简单示例

    这篇文章主要为大家详细介绍了python装饰器的简单示例,具有一定的参考价值,可以用来参考一下. 对python这个高级语言感兴趣的小伙伴,下面一起跟随512笔记的小编两巴掌来看看吧! 装饰器的语法以 ...

  8. python装饰设备_python装饰器

    无参装饰器: 它是一个函数,函数作为它的形参,返回值也是一个函数,可以使用@functionname方式,简化调用 装饰器和高阶函数: 装饰器是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强) ...

  9. 学习python装饰器_Python装饰器学习(九步入门)

    这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 # -*- coding:gbk -*- '''示例1: 最简单的函数,表示调用了两次 ...

最新文章

  1. 网络推广专员如何通过社交能力扩宽外链渠道助力网络推广?
  2. C++中绘图工具EasyX基本操作
  3. dlgdata.cpp错误提示 解决方案
  4. Pytest跳过执行之@pytest.mark.skip()详解大全
  5. thinkphp5.0l路由冲突原因及解决方法
  6. mysql5.7安装教程
  7. pat1057 stack
  8. 《深入理解Java虚拟机》读书笔记十二
  9. xyplorer保存设置失败_腾讯企点客服如何设置敏感词监控?
  10. DataMining with Sql 2005
  11. (转)MapReduce源码分析总结
  12. 勤哲excel服务器点击修改,用勤哲Excel服务器实现工作任务管理系统
  13. RUOK的完整形式是什么?
  14. 人生有三重境界:看山是山,看水是水;看山不是山,看水不是水;看山还是山,看水还是水=
  15. 浏览器不支持 flash 插件
  16. 多个正方体叠加所得立体图形的表面积
  17. gmail 邮箱附件大小 突破10M
  18. 嵌入式系统python开发_嵌组词_嵌的拼音含义_组词造句解释_嵌字的组词
  19. win10 3D viewer
  20. java电信计费项目论文_电信计费系统的设计与实现毕业论文.doc

热门文章

  1. 服务器时间延迟,如何处理从服务器延迟响应时间'力逼近'
  2. tohexstring方法_Java Float类toHexString()方法的示例
  3. Java ClassLoader getSystemResource()方法与示例
  4. NHibernate使用之详细图解
  5. QT5开发的程序打包发布
  6. SpringBoot执行器端点Actuator Endpoint
  7. Win10系统java环境配置
  8. 刀剑无双服务器显示404,刀剑无双如何开启GM命令 刀剑无双GM指令修改
  9. 谷歌发布最新版安卓Android,谷歌发布安卓 9 正式版,代号 Android Pie
  10. 启动盘Linux windows,Linux 中创建 USB 启动盘来拯救 Windows 用户