Python学习笔记012——装饰器
1 装饰器
1.1装饰器定义
在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
1.2 装饰器分类
装饰器:函数装饰器,类装饰器,函数的装饰器,类的装饰器
装饰器:函数装饰函数,函数装饰类,类装饰函数,类装饰类(两者不是一一对应关系,其实我也不知道他们之间是什么样的对应关系)
一般而言,主要应用的是函数装饰函数
1.3 装饰器本质
本质上,decorator就是一个返回函数的高阶函数。
在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,
而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。
Python的decorator可以用函数实现,也可以用类实现。
1.4 装饰器目的
在不改变对象名的情况下,丰富原对象的功能
1.5 装饰器的作用
1)多个函数对象需要添加同一个功能,将该功能写成一个函数,该函数就是装饰函数,在任何函数需要该功能时则直接调用该函数即可,也即,我们用装饰函数对其装饰
2)被装饰的函数我们称之为功能函数,所以功能函数在装饰后,尽管函数内容发生了改变,但是功能函数的名还需要保留。
2 装饰器应用实例
该部分主要分为:
简单装饰器,功能函数中带有返回值的装饰函数,多个功能函数中参数个数不固定的装饰器,带参数的装饰器,装饰器的嵌套
简而言之:
简单装饰器,功能函数带返回值的解决办法,功能函数参数不定的解决办法,装饰器中参数的解决办法,装饰器的嵌套解决办法
2.1 装饰器雏形
实例:
def fun1(fun2): print("befor fun1 called") fun2() print("after fun1 called") return fun2 def fun3(): print("fun3 called") fun3() fun1(fun3)
输出:
fun3 called befor fun1 called fun3 called after fun1 called
在这里,功能函数为 fun3 ,装饰器为 fun1
在原本功能函数中增加了两个输出代码。
在代码中,注意区分 fun3 与 fun3() 之间的关系: fun3理解成函数变量, fun3()理解成函数调用。采用 id() 判断id位置即可验证。
为了更说明问题,我们同时验证测试 fun1与 fun1()之间的关系
def fun1(fun2): print("befor fun1 called") fun2() print("after fun1 called") return fun2 def fun3(): print("fun3 called") fun3() print("---------------------") fun1(fun3) print("---------------------") print(id(fun3)) print("---------------------") print(id(fun3())) print("---------------------") print(id(fun1)) print("---------------------") print(id(fun1()))
输出:
fun3 called --------------------- befor fun1 called fun3 called after fun1 called --------------------- 140504457497728 --------------------- fun3 called 10748000 --------------------- 140504482160424 --------------------- Traceback (most recent call last): File "test0.py", line 20, in <module> print(id(fun1())) TypeError: fun1() missing 1 required positional argument: 'fun2'
此时,我们可以明确两个问题
1) fun3 与 fun3() 不是一个概念, fun3 为函数变量, fun3() 是函数调用。
2)在 fun1() 中的形参 fun2 仅仅传递了函数参数,没有调用 fun2() ,所以在调用 fun1() 时执行函数内代码时才调用了 fun2()
另外:这里并没有完全实现装饰器的作用,仅是简单的一个装饰器演示,因被装饰器修改后,调用原功能函数时并没有改变其函数内容
2.2 简单装饰器
def fun1(fun2): def fun4(): print("befor fun1 called") fun2() print("after fun1 called") return fun4 @fun1 #fun3=fun1(fun3) def fun3(): print("fun3 called") print(fun3())
输出
befor fun1 called fun3 called after fun1 called None
注:
1)在输出最后出现 None ,主要是因为 fun3() 函数(经过装饰后)没有返回值(也可以理解成返回值为空)。
将 print(fun3()) 修改为 fun3() ,则就不会输出 None
2) @fun1 表示 fun3 = fun1(fun3) ,但是不能被描述为等同于 fun3() = fun1(fun3) ,注意区分 fun3与 fun3()之间的关系
2.3 功能函数中带有返回值
def fun1(fun2): def fun4(): print("befor fun1 called") a = fun2() print("after fun1 called") return a return fun4 @fun1 #fun3=fun1(fun3) def fun3(): print("fun3 called") return "YES" fun3 print("---------------------1") fun3() print("---------------------2") print(fun3) print("---------------------3") print(fun3())
输出:
---------------------1 befor fun1 called fun3 called after fun1 called ---------------------2 <function fun1.<locals>.fun4 at 0x7f930b77e048> ---------------------3 befor fun1 called fun3 called after fun1 called YES
解决返回值的时候,其实就是先在装饰器函数接受功能函数中的返回值,再在装饰器中将该返回值 return
2.4 功能函数参数个数确定的解决办法
def fun1(fun2): def fun4(m,n): print("befor fun1 called") a = fun2(m,n) print("after fun1 called") return a return fun4 @fun1 #fun3=fun1(fun3) def fun3(i,j): print("fun3 called") return i * j print(fun3(2,3))
输出:
befor fun1 called fun3 called after fun1 called 6
2.5 功能函数参数个数不确定的解决办法
def fun1(fun2): def fun4(*m,**n): print("befor fun1 called") a = fun2(*m,**n) print("after fun1 called") return a return fun4 @fun1 #fun3=fun1(fun3) def fun3(i,j): print("fun3 called") return i + j @fun1 def fun5(i,j,k): print("fun5 called") return i + j + k print(fun3(1,2)) print(fun5(1,k=2,j=3))
输出:
befor fun1 called fun3 called after fun1 called 3 befor fun1 called fun5 called after fun1 called 6
采用元组与字典混合形式可以将所有可变参数一网打尽
2.6装饰器中参数的解决办法
有时候需要在装饰器中增加参数,解决的办法就是在原装饰器的最外层多增加一层函数嵌套即可
def fun6(args): def fun1(fun2): def fun4(*m,**n): print("befor fun1 called:[%s]"%args) a = fun2(*m,**n) print("after fun1 called >> %s"%args) return a return fun4 return fun1 # fun1 = fun6("耕毅的学习笔记") # fun4 = fun1(fun3) # fun3 = fun4 #综上所述可以简写为 # fun3 = fun6('耕毅学习笔记')(fun3) @fun6("耕毅学习笔记") def fun3(i,j): print("fun3 called") return i + j @fun6("耕毅练习笔记") def fun5(i,j,k): print("fun5 called") return i + j + k print(fun3(1,2)) print(fun5(1,k=2,j=3))
输出:
befor fun1 called:[耕毅学习笔记] fun3 called after fun1 called >> 耕毅学习笔记 3 befor fun1 called:[耕毅练习笔记] fun5 called after fun1 called >> 耕毅练习笔记 6
2.7 装饰器的嵌套解决办法
如果一个装饰器满足功能函数需求,则可以增加一个装饰器再对其修饰。 该种方法不常用
def fun1(fn): print("fun1 called") def fun2(fn): print("fun2 called") @fun1 @fun2 def fun3(): print("fun3 called") fun3()
输出
deco2 is called deco1 is called Traceback (most recent call last): File "test0.py", line 28, in <module> myfun() TypeError: 'NoneType' object is not callable
1)该段代码说明装饰器也可以嵌套,嵌套关系
@fun2 fun3 = fun2(fun3) @fun1 fun3 = fun2(fun3)
该嵌套关系先执行 @fun2 再执行 @fun1
其实在装饰器概念中含有了“闭包”内容。
最新文章
- Matlab学习笔记(一)—— 三维图形绘制
- RSA For PHP
- Ubuntu 14.04 载入 JWS 或 访问 jsp异常的解决方法
- 转:UniqueID和ClientID的来源
- Docker仓库管理
- SPRING IN ACTION 第4版笔记-第十一章Persisting data with object-relational mapping-005Spring-Data-JPA例子的代码
- 锁大全与 GDB调试
- html&;css&;js随笔-问题集锦
- 升级iOS10之后调用摄像头/麦克风等硬件程序崩溃闪退的问题
- HW4.22
- Linux和windows下清除svn保存的账号密码信息
- assert实现
- (原) Jquery 判断设备是PC端,还是移动端
- hdu1003 1024 Max Sum&;Max Sum Plus Plus【基础dp】
- QT添加exe文件的图标LOGO
- [干货来袭]C#7.0新特性(VS2017可用)
- HBase shell scan 过滤器用法总结
- js篇-解析url链接里面的参数名和参数值
- VC++6.0的使用感想
- XCode中常用错误解决