python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象(函数的指针)。装饰器函数的外部函数传入我要装饰的函数名字,返回经过修饰后函数的名字;内层函数(闭包)负责修饰被修饰函数。
python装饰器有很多经典的应用场景,比如:插入日志、性能测试、事务处理、权限校验等。装饰器是解决这类问题的绝佳设计。并且从引入中的列子中我们也可以归纳出:装饰器最大的作用就是对于我们已经写好的程序,我们可以抽离出一些雷同的代码组建多个特定功能的装饰器,这样我们就可以针对不同的需求去使用特定的装饰器,这时因为源码去除了大量泛化的内容而使得源码具有更加清晰的逻辑。
说明:
import datetime
def hello(fun):
def preHello():
print("#####start#####")
fun()
print("#####end#####")
return preHello
@hello
def myTime():
now = datetime.datetime.now()
print(now)
myTime()
输出:
#####start#####
2018-04-18 21:36:02.621744
#####end#####
分析:
1、装饰器是什么?
装饰器,顾名思义,就是用来“装饰”的。
@XXX
也就是如上的@hello
# 1. @hello = myTime = hello(myTime)
# 2. 调用hello()
# 装饰器通过@进行使用 ,相当于把myTime() 函数作为参数,传给hello()
# @hello 相当于 myTime = hello(myTime)
# 当你调用myTime()的时候,是不是就相当于调用了hello(myTime())()
import datetime
def hello(fun):
def preHello(): ##2
print("#####start#####") ##6
fun() ##7 ##10
print("#####end#####") ##11
return preHello ##3
@hello ##1 ##4
def myTime():
now = datetime.datetime.now() ##8
print(now) ##9
myTime() ##5 ##12
每一层 def 获得一个 (xxx)
def deco_with_arg(第一个括号内的参数): # 一般是装饰器的参数
def deco(第二个括号内的参数): # 一般是 func 本身
def wrapper(第三个括号内的参数): # 一般是 func 调用时的参数
# 调用 func
return wrapper
return deco
def startEnd(fun):
def wraper(name):
print("!!!!!!!start!!!!!!!!")
fun(name)
print("!!!!!!!end!!!!!!!!")
return wraper
# 返回值 wraper函数
# hello() 相当于执行 wraper()
@startEnd
def hello(name):
print("hello {0}".format(name))
hello("aaaa")
# 1. a = startEnd(hello)
# 2. hello = a
# 3. 调用hello()
# 装饰器的作用:在不改变源代码的情况下,给现有的函数增加新的功能
# 装饰器通过@进行使用 ,相当于把hello() 函数作为参数,传给startEnd()
# @startEnd 相当于 hello = startEnd(hello)
# 当你调用hello()的时候,是不是就相当于调用了startEnd(hello())()
日常生活中有这种情况,商场刚装修完,都是大玻璃门,怕有人撞上,就要在上面用油漆写“小心琉璃”……但是玻璃太多,都写上字太费劲,干脆就用油漆画个叉就达到目的了。用油漆简单装饰一下,完成省得有人撞上和指路的功能。
“装饰器”就是做个装修标记,并且有它的功能,比如“小心玻璃”和“指路”,看到这个标记你就知道它要表达什么意思了,按着它的指示来肯定没错。
在Python里装饰器的定义:在程序运行时,增加动态功能的方式,称之为“装饰器”,装饰器本质上也是一个Python函数。
那么问题来了,有可能初学者对这个定义里的函数不理解,不得不多讲一点。
函数,“数”好理解,“函”本意就是一种平级之间的信,比如两个单位传达或者反馈信息。
假设有这样一种情况,你要出去旅游购物,又怕被黑,在出发前,找到好朋友诸葛不亮出主意,他给了你一个密函,告诉你到地方再打开。等你到地方打开一看,上写5个大字“报价砍一半”。接下来买什么东西,都按这个原则来,这个就是最基本的函数了。
写成公式:购买价格=对方报价乘0.5,这个公式就是函数公式。用标准定义来说“设在某变化过程中有两个变量x、y,如果对于x在某一范围内的每一个确定的值,y都有唯一确定的值与它对应,那么就称y是x的函数,x叫做自变量。”来段简单的代码感受一下这个“讲价函数”。
为了方便初学者,本文所有函数名称都使用全拼。
def Jianjia(x):
y =0.5
x = x * y
return x
print(Hanshu(100))
print(Hanshu(50))
输出结果,你猜是不是50和25?我猜你猜对了。
def是固定格式,Hanshu就是函数的名称,(x)就是参数。
接下来,就要旅游购物了,好在有个机器人替我购物,它只能听懂Python语言,我要告诉它购买什么和详细的购买程序。
我要买三样东西:猪、大象、长颈鹿,流程是询价,对应东西,再购买。
有三个购买函数(Goumai_1,Goumai_2,Goumai_3),写出来的流程是这样:
def Goumai_1():
print('询价')
print('猪')
print('购买成功')
def Goumai_2():
print('询价')
print('大象')
print('购买成功')
def Goumai_3():
print('询价')
print('长颈鹿')
print('购买成功')
Goumai_1()
Goumai_2()
Goumai_3()
执行结果是这样:
询价
猪
购买成功
询价
大象
购买成功
询价
长颈鹿
购买成功
现在我发现,这样写太复杂了。因为“询价”和“购买成功”动作是一样的,而且我要想在“购买成功”上加一个感叹号,需要加三次才能成功,如果买1000个东西,就要加1000次感叹号了,想想就要累死了。
于是我优化了一下程序,把购买东西Goumai(Dongxi)定义成了一个函数,执行的结果和上一个程序还是一样的,而且“购买成功”还加了一个感叹号,只操作了一次,可以显示三次啊。
def Goumai(Dongxi):
print('询价')
Dongxi()
print('购买成功!')
def Zhu():
print('猪')
def Daxiang():
print('大象')
def Changjinglu():
print('长颈鹿')
Goumai(Zhu)
Goumai(Daxiang)
Goumai(Changjinglu)
但是问题又来了,每次我还要告诉机器人“购买(猪)”,“购买(大象)”,“购买(长颈鹿)”,能不能干脆点“购买”两个字都不说?直接在“猪、大象、长颈鹿”上做个标记,机器人看到标记就执行购买动作呢?答案当然是可以了。
这时“购买”就变成了一个固定的动作,而不是三个步骤,为了让机器人理解,我在“购买”函数里直接定义了这个动作函数。
def Goumai(Dongxi):
def Dongzuo():
print('询价')
Dongxi()
print('购买成功!')
return Dongzuo
这个时候装饰器才正式出场,装饰器就是用@来表示,加上动作函数名称。
比如下文中的,漂亮吗?
@Goumai
可以理解成用@符号把“购买”这个两个字像用口香糖粘在了物品名称上一样,这回有点像“装饰”的意思了吧。
机器人看到@的标签,就会按@里的动作来执行。
代码如下:
def Goumai(Dongxi):
def Dongzuo():
print('询价')
Dongxi()
print('购买成功!')
return Dongzuo
@Goumai
def Zhu():
print('猪')
@Goumai
def Daxiang():
print('大象')
@Goumai
def Changjinglu():
print('长颈鹿')
Zhu()
Daxiang()
Changjinglu()
再结合一下刚才的讲价函数,先询价,再讲价,再购买,就可以写成这样:
def Goumai(Dongxi):
def Dongzuo():
print('询价')
Dongxi()
print('购买成功!')
return Dongxi
return Dongzuo
def JangJia(x):
y =0.5
x = int(x) * y
return x
@Goumai
def Zhu():
Baojia = input('请输入对方报价:')
Huanjia = JangJia(Baojia)
print('猪:' + str(Huanjia))
@Goumai
def Daxiang():
Baojia = input('请输入对方报价:')
Huanjia = JangJia(Baojia)
print('大象:' + str(Huanjia))
@Goumai
def Changjinglu():
Baojia = input('请输入对方报价:')
Huanjia = JangJia(Baojia)
print('长颈鹿:' + str(Huanjia))
Zhu()
Daxiang()
Changjinglu()
执行后的结果:
询价
请输入对方报价:100
猪:50.0
购买成功!
询价
请输入对方报价:50
大象:25.0
购买成功!
询价
请输入对方报价:25
长颈鹿:12.5
购买成功!
def startEnd(author):
def a(fun):
def b(name):
print("this author is {0}".format(author))
print("!!!!!!!start!!!!!!")
fun(name)
print("!!!!!!!!end!!!!!!!")
return b
return a
@startEnd("zhdya")
def hello(name):
print("hello {0}".format(name))
# hello = startEnd("zhdya")(hello)
# hello("AAAAA")
hello("AAAAA")