理解装饰器
装饰器就是在不修改源代码和调用方式的基础上给其增加新的功能,并且多个装饰器可以装饰在同一个函数上。
我们可以一步一步构建一个模型来理解装饰器。
我们现在有一个函数,我们称其为1#函数,现在我们要实现装饰器所提到的功能,即不改变源代码并增加新的功能。虽然我们现在没有思路,但是我们可以先把新的功能函数写出来,我们称其为2#函数。现在功能上的问题解决了,我们需要把两个函数合并,既然不能动1#,那我们只能把1#合并到2#下面,也就是当执行2#时既会执行2#,也会顺便执行1#;并且因为不能改变调用方式,我们需要之前执行1#的命令现在去执行2#。解决这两个问题,我们的装饰器就完成了。在思考时,我们可以把问题写在纸上。
- 原本执行1#的命令要去执行2#
- 在执行2#的同时执行1#
实现装饰器
1#函数
def hello():
print('hello')
2#函数
def world():
print('world')
最终符合要求的结果(大家最好不要像我这样用脚起名)
def hello():
print('hello')
def world(func):
def helloworld():
func()
print('world')
return helloworld
hello = world(hello)
hello()
我们可以看到原函数并无改变,执行函数的命令也没有改变,满足了要求,那么问题是怎样解决的
解决第一个问题
def hello():
print('hello')
def world(func):
def helloworld():
func()
print('world')
return helloworld
print(hello)
hello = world(hello)
print(hello)
<function hello at 0x00000169CF612E18>
<function world.<locals>.helloworld at 0x00000169D12E8B70>
我们看到函数hello发生了变化,此时执行hello()已经不再是执行hello函数,而是执行了helloworld函数。
我们比较一下
hello()
world(hello())
这两个命令在这里的作用是一样的
解决第二个问题
既然我们转而去执行helloworld函数了,那么我们来看看helloworld函数是怎么样的
def world(func):
def helloworld():
func()
print('world')
return helloworld
这里的func就是hello,也就是说,2#函数变成了这样:
def world(hello):
def helloworld():
hello()
print('world')
return helloworld
此时我们成功实现了使2#函数执行同时把1#函数也执行一遍。
装饰器封装
def world(func):
def helloworld():
func()
print('world')
return helloworld
@world
def hello():
print('hello')
hello()
我们一般会将装饰器写成这种格式。也就是说,当看到@……的时候,省略号代表装饰器,而下面的内容是源函数。
当源函数携带变量
如果源函数变成了这样呢
def hello(name):
print('hello %s'%name)
hello('world')
当你理解了之前解决的第二个问题之后,这根本是迎刃而解
def world(func):
def helloworld(name):
func(name)
print('world')
return helloworld
@world
def hello(name):
print('hello %s'%name)
hello('world')
写在最后
很多Python初学者一到装饰器就懵逼,如果只看概念可能确实难以理解,这个时候的学习就应该换种思路,不去想它为什么要这么做,而是想正常情况下我们应该怎么做,当从一头向另一头推导很困难的时候,就换一个方向。