疑问
有没有一种方式,在不改变代源码、不改变调用方式的情况下,对函数增加功能。添加一些我们需要的方法、逻辑。
这时候装饰器就出现了。装饰器,看其名知其意。在不改变原有结构的情况下,让其本身具有之前不具有的功能。
实现装饰器的知识储备:
- 函数即“变量”
- 高阶函数
把一个函数名当做实参传递给另外一个函数
返回值中包含函数名 - 嵌套函数
在函数体内,使用def再声明一个函数。
装饰器= 高阶函数 + 嵌套函数
装饰器
定义:装饰器本质是函数(装饰其他函数)就是为其他函数添加附加功能。
原则:
- 1、不能修改被装饰函数的源代码
- 2、不能修改被装饰函数的调用方式
可以总结成一句话:悄无声息的为这个函数增加功能。
一个简单的装饰器:
#我们想让test函数在打印出一条“大师兄”
def decoretor(func):
def warpper(*args,**kwargs):
print("大师兄")
func()
return warpper
@decoretor
def test():
print("孙悟空")
test()
解读一下上面的代码,在我们的定义里 【装饰器本质是函数】,通过以上代码我们可以看出来,我们定义decoretor()本身就是函数。符合我们的定义。然后装饰器的原则是 【不能修改被装饰函数的源代码】【不能修改被装饰函数的调用方式】,很显然,我们没有在test中对其源代码进行修改,我们也没有改变test的调用方式。
解惑
我们按照【装饰器的知识储备】一一分析。
-
函数即“变量”
变量都会放在内存中。
例如:x=1,1会存在在内存中,x会与内存形成一个映射关系,x指向内存1的地址。如下图所示
【函数即“变量”】我们可以理解函数在内存中的存放方式和x=1的是一样的,函数体在内存中,函数名与函数体一一映射。
我们如果想要运行这个函数,直接调用即可test(),但是调用函数跟调变量是有区别的,函数必须在函数名后面加上“()”,如果不加“()”,那么我们引用的则是test的内存地址值def test(): print ("孙悟空") test() # 孙悟空 print(test) # <function test at 0x000002546796F400>
甚至可以更通俗的讲,我们把函数就赋值给变量,在我们调用变量的时,也就是在调用函数
def test(): print ("孙悟空") x = test x()#孙悟空
-
高阶函数
- 把一个函数名当做实参传递给另外一个函数
- 返回值中包含函数名
我们按照高阶函数的定义写一个函数
def test1(func): return func def test2(): print ("孙悟空") x = test1(test2) x()#孙悟空
看上面的代码,我们可以理解为test1()函数的返回值是test2这个函数的地址值,x=test1(test2)可以改写为x=test2,当我们打印x()的时候,相当于x()=test2(),相当于打印test2函数的返回值。所以记过为“孙悟空”
这个时候我们可以模仿着写一个装饰器def decoretor(func): print("大师兄") func() return func def test(): print ("孙悟空") decoretor(test)
看这个结果符合我们的需求,在打出“孙悟空”之前打出"大师兄"这句话。看着想那么回事,我们没有改变test()函数源码,但是我们改变了函数的调用方式,所以这个看似像装饰器的函数,并不是我们想要的结果。
那么我们再继续修改:def decoretor(func): print("大师兄") return func def test(): print ("孙悟空") # x = decoretor(test) # test=x test = decoretor(test) test()
这样我们就可以不改变源代码,不改变调用方式,就可以对test()函数进行修改。
写到这里,我们用了【函数即“变量”】,用了【高阶函数】,是不是我们就可以这样写装饰器了呢?不!【装饰器= 高阶函数 + 嵌套函数】,我们还没有用【嵌套函数】!
- 嵌套函数
在函数体内,使用def再声明一个函数。
通过嵌套函数,我们把上面代码再次进行修改def test1(): def test2(): pass
python的装饰器原理就是这样,但是这个我们头开始的代码不一样,头开始的代码中带有@,而我们现在的代码没有@,其实@是python为我们提供的一个语法糖,将@放在待修饰的函数上面,这样我们的调用的时候就不用写【test = demo(test)】,直接在下面调用函数即可def demo(func): def decoretor(): print("大师兄") func() return decoretor def test(): print ("孙悟空") test = demo(test) test()
讲完这几个之后,我们在往回看看上面写的装饰器,想必大家已经豁然开朗了吧。