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
其实在装饰器概念中含有了“闭包”内容。
Python学习笔记012——装饰器的更多相关文章
- Python学习笔记:装饰器
Python 装饰器的基本概念和应用 代码编写要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即: 封闭:已 ...
- python学习笔记:装饰器2
python的装饰器本质是函数,为了不改变装饰目标函数内部代码而增加额外功能而存在 一.一般装饰函数实例: import datetime def func_name(func):#定义一个装饰函数, ...
- python学习笔记(五):装饰器、生成器、内置函数、json
一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里 ...
- python学习笔记之装饰器、生成器、内置函数、json(五)
一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里面 ...
- python学习笔记之装饰器、递归、算法(第四天)
参考老师的博客: 金角:http://www.cnblogs.com/alex3714/articles/5161349.html 银角:http://www.cnblogs.com/wupeiqi/ ...
- Python学习笔记之装饰器原理
def decorator(fn): def wrapper(): print("询价") fn() print("购买成功!") return wrapper ...
- python学习之day5,装饰器,生成器,迭代器,json,pickle
1.装饰器 import os import time def auth(type): def timeer(func): def inner(*args,**kwargs): start = tim ...
- Python学习——迭代器&生成器&装饰器
一.迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素.迭代器仅 ...
- 从零开始的Python学习Episode 11——装饰器
装饰器 装饰器是用来处理其他函数的函数,主要作用是在不修改原有函数的情况下添加新的功能,装饰器的返回值也是一个函数对象. 简单的装饰器 import time def show_time(f): de ...
随机推荐
- C#读写txt文件的两种方法介绍[转]
C#读写txt文件的两种方法介绍 1.添加命名空间 System.IO; System.Text; 2.文件的读取 (1).使用FileStream类进行文件的读取,并将它转换成char数组,然后输出 ...
- TPC-E在populate测试Database时需要注意的一些事项
第一, 安装时不要使用named instance, 默认的instance就好. 否则会报连不上Database. 第二, TPC-E工具文件夹的完整路径中不可以有空格, 否则会在generate ...
- TabLayout ViewPager Fragment 简介 案例 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- JS字符串false转boolean
大家都知道在JS的世界里, 0.-0.null."".false.undefined 或 NaN,这些都可以自动转化为布尔的 false,那么字符串的"false&quo ...
- 【python】理想论坛帖子爬虫1.06
昨天认识到在本期同时起一百个回调/线程后程序会崩溃,造成结果不可信. 于是决定用Python单线程操作,因为它理论上就用主线程跑不会有问题,只是时间长点. 写好程序后,测试了一中午,210个主贴,11 ...
- iOS开发系列课程预告
近期在Mac和iOS上做开发,认为应该写一点东西分享给感兴趣的童鞋们.在此之前.以前有非常多同行们都在埋怨苹果Objective-C的复杂和难以上手,为此也有非常多人对今年(2014年)刚推出的Swi ...
- ArcGIS高程Z值的去除方法
在ArcGIS中,我们常用的几何类型有点.线.面.体(体,在涉及三维的情况下使用),但在这之外,可能会遇到带ZM的类型,如图所示,面ZM,它与面类似,但比面多两个字段. 在编辑中查看草图属性可看到Z值 ...
- 破解OfficeVBA密码的方法
我自己找到一个office的VBA加密方法,然后再去找一个方法来破解密码,好像有点自相矛盾啊. 如果excel文件是xls或xlm格式(如果不是请转化成此种方法),则可使用以下代码: '移除VBA编码 ...
- js条件语句之职责链数组
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- UVa 10642 - Can You Solve It?
题目:二维平面上的整数点.用路径链接起来(0,0)->(1.0)->(0.1)->(2,0)->.. 给你两点坐标.求两点间步长(在路径上的距离). 分析:简单题. 我们发现点 ...