不懂指针就不能说自己懂C,那么不懂装饰器,就不能说自己会python。上篇文章说过闭包是用来实现装饰器的,算是有了一个铺垫。
装饰器从字面意思来理解的话,就是我们对一件事物的装饰。那么我们什么时候选择去装饰一件事物呢?当然是这件事物本身无法满足我们的审美,我们要用一些添加一些装饰器来满足我们的需求。那么结合软件开发的场景。我们有的时候写完一个类,一个方法或者函数,忽然发现功能得不到满足。按照软件开发的开放封闭原则,最好原则上不要修改我们已经写过的代码,因为这毕竟不是重构,怎么办?这时候装饰器就起作用了,装饰器的作用就是为它所装饰的方法添加一些新的功能。所以说,装饰器这个翻译恰到好处,见名知意。装饰器本身对我们来说有巨大的用处,下面结合代码进行讲解。
def set_func(func):
def call_func():
print("这是权限验证1")
print("这是权限验证2")
func()
return call_func
@set_func
def test1():
print("----test1----")
test1()
运行结果:
是否感到很神奇?接下来一步一步来分析。首先set_func结合上篇文章可以看出是一个闭包,那么返回的是什么呢?记得上篇文章说过如果是函数名不加括号,相当于返回了函数本身,函数本身是一个变量也是一个对象也是对该函数本身的引用。接着看,到最后test1()函数调用的时候竟然,真的把装饰器里面要加的东西打印出来了,那么此时此刻我有一个大胆的想法,我们是否可以手动的实现装饰器呢?答案是肯定的,下面上代码:
def set_func(func):
def call_func(num2):
print("这是权限验证1")
print("这是权限验证2")
func(num2)
return call_func
def test1(num):
print("----test1----%d"%num)
test1 = set_func(test1)
test1(100)
运行结果:
哇 真的实现了!下面讲解。test1= set_func(test1)执行之后,我问一个问题,最后test1是包含了谁的引用?答案是test1。首先,set_func返回了call_func的引用。然后,注意一个细节,我们把test1函数本身也传递了进去,所以,当执行test1(100)这个操作的时候,实际上先调用了call_func函数,打印结束后,调用了func函数,但是此时我们知道,由于闭包的特性,func此时调用的就是test1本身,所以最后的打印结果和我们加了装饰器的效果一模一样,这就是装饰器的手动实现。所以我们下次再看见装饰器的时候,就可以认为他执行了以上操作。、
那么下一个 当装饰器修饰多参数的环境时呢,不多说上代码:
ef set_func(func):
def call_func(*args,**kwarg):
print("这是权限验证1")
print("这是权限验证2")
func(*args,**kwarg)
return call_func
@set_func
def test1(*args,**kwarg):
print("----test1----",args)
print("----test1----", kwarg)
test1(100,200,300,mm=100)
一切都是水到渠成。那么再想一个问题,当同一个装饰器修饰不同函数时可以吗?当然可以,看代码:
from time import ctime, sleep
def timefun(func):
def wrapped_func():
print("%s called at %s" % (func.__name__, ctime()))
func()
return wrapped_func
@timefun
def foo():
print("I am foo")
@timefun
def getInfo():
return '----hahah---'
foo()
sleep(2)
foo()
print(getInfo())
运行结果:
foo called at Fri Nov 4 21:55:35 2016
I am foo
foo called at Fri Nov 4 21:55:37 2016
I am foo
getInfo called at Fri Nov 4 21:55:37 2016
None
可以看出是可以的,那么下面再引出一个问题,有返回值的怎么办?所以为了让装饰器更加通用,最好在func处改为return func(),这样会更加通用。
上面说了同一个装饰器修饰多个函数可以的,那么多个修饰器修饰同一个方法怎么办?代码如下:
def add_qx(func):
print("---开始进行装饰权限1的功能---")
def call_func(*args, **kwargs):
print("---这是权限验证1----")
return func(*args, **kwargs)
return call_func
def add_xx(func):
print("---开始进行装饰xxx的功能---")
def call_func(*args, **kwargs):
print("---这是xxx的功能----")
return func(*args, **kwargs)
return call_func
@add_qx
@add_xx
def test1():
print("------test1------")
test1()
结果如下:
可以总结出一个规律,当有多个修饰器修饰同一函数时,执行结果是从上到下顺序执行的。
那么在引申一下,装饰器可否装饰类?答案是可以的,代码如下:
class Test(object):
def __init__(self, func):
self.func = func
def __call__(self):
print("这里是装饰器添加的功能.....")
return self.func()
@Test # 相当于get_str = Test(get_str)
def get_str():
return "haha"
print(get_str())
运行结果:
此种不太常用,这里再介绍一个知识点,__call__方法,可以直接使用类名加括号使用,就和使用函数一样。是不是很灵活。今天装饰器讲解到此结束