装饰器其实一直是个人一个"老大难"。这个知识点就放在那,可是拖延症。。。python
其实在日常写写脚本的过程当中,这个知识点你可能用到很少面试
但在面试的时候,这但是一个高频问题。app
所谓的装饰器,其实就是经过装饰器函数,来修改原函数的一些功能,使得原函数不须要修改。函数
这一句话理解起来可能没那么轻松,那先来看一个"傻瓜"函数。code
放心,绝对不是"Hello World"!orm
def hello(): print("你好,装饰器")
肿么样,木骗你吧? 哈哈,这个函数不用运行相信你们都知道输出结果:"你好,装饰器"
。it
那若是我想让hello()
函数再实现个其余功能,好比多打印一句话。form
那么,能够这样"加强"一下:class
def my_decorator(func): def wrapper(): print("这是装饰后具备的新输出") func() return wrapper def hello(): print("你好,装饰器") hello = my_decorator(hello) hello()
运行结果:import
这是装饰后具备的新输出 你好,装饰器 [Finished in 0.1s]
很显然,这个"加强"没啥做用,可是能够帮助理解装饰器。
当运行最后的hello()
函数时,调用过程是这样的:
hello = my_decorator(hello)
中,变量hello指向的是my_decorator()
my_decorator(func)
中传参是hello
,返回的wrapper
,所以又会调用到原函数hello()
wrapper()
函数里的,而后才打印出hello()
函数里的那上述代码里的my_decorator()
就是一个装饰器。
它改变了hello()
的行为,可是并无去真正的改变hello()函数
的内部实现。
可是,python一直以"优雅"被人追捧,而上述的代码显然不够优雅。
因此,想让上述装饰器变得优雅,能够这样写:
def my_decorator(func): def wrapper(): print("这是装饰后具备的新输出") func() return wrapper @my_decorator def hello(): print("你好,装饰器") hello()
这里的@my_decorator
就至关于旧代码的hello = my_decorator(hello)
,@
符号称为语法糖。
那若是还有其余函数也须要加上相似的装饰,直接在函数的上方加上@my_decorator
就能够,大大提升函数
的重复利用与可读性。
def my_decorator(func): def wrapper(): print("这是装饰后具备的新输出") func() return wrapper @my_decorator def hello(): print("你好,装饰器") @my_decorator def hello2(): print("你好,装饰器2") hello2()
输出:
这是装饰后具备的新输出 你好,装饰器2 [Finished in 0.1s]
上面的只是一个很是简单的装饰器,可是实际场景中,不少函数都是要带有参数的,好比hello(people_name)。
其实也很简单,要什么咱们就给什么呗,直接在对应装饰器的wrapper()
上,加上对应的参数:
def my_decorator(func): def wrapper(people_name): print("这是装饰后具备的新输出") func(people_name) return wrapper @my_decorator def hello(people_name): print("你好,{}".format(people_name)) hello("张三")
输出:
这是装饰后具备的新输出 你好,张三 [Finished in 0.1s]
可是还没完,这样虽然简单,可是随之而来另外一个问题:由于并非全部函数参数都是同样的,
当其余要使用装饰器的函数参数不止这个一个肿么办?好比:
@my_decorator def hello3(speaker, listener): print("{}对{}说你好!".format(speaker, listener))
不要紧,在python里,*args
和**kwargs
表示接受任意数量和类型的参数,因此咱们能够这样
写装饰器里的wrapper()
函数:
def my_decorator(func): def wrapper(*args, **kwargs): print("这是装饰后具备的新输出") func(*args, **kwargs) return wrapper @my_decorator def hello(people_name): print("你好,{}".format(people_name)) @my_decorator def hello3(speaker, listener): print("{}对{}说你好!".format(speaker, listener)) hello("老王") print("------------------------") hello3("张三", "李四")
同时运行下hello("老王")
,和hello3("张三", "李四")
,看结果:
这是装饰后具备的新输出 你好,老王 ------------------------ 这是装饰后具备的新输出 张三对李四说你好! [Finished in 0.1s]
上面2种,装饰器都是接收外来的参数,其实装饰器还能够接收本身的参数。
好比,我加个参数来控制下装饰器中打印信息的次数:
def count(num): def my_decorator(func): def wrapper(*args, **kwargs): for i in range(num): print("这是装饰后具备的新输出") func(*args, **kwargs) return wrapper return my_decorator @count(3) def hello(people_name): print("你好,{}".format(people_name)) hello("老王")
注意,这里count
装饰函数中的2个return
.
运行下,应该会出现3次:
这是装饰后具备的新输出 你好,老王 这是装饰后具备的新输出 你好,老王 这是装饰后具备的新输出 你好,老王 [Finished in 0.1s]
@functools.wrap
如今多作一步探索,咱们来打印下下面例子中的hello()函数的元信息:
def my_decorator(func): def wrapper(*args, **kwargs): print("这是装饰后具备的新输出") func(*args, **kwargs) return wrapper @my_decorator def hello(people_name): print("你好,{}".format(people_name)) print(hello.__name__) #看下hello函数的元信息
输出:
wrapper
这说明了,它再也不是之前的那个 hello()
函数,而是被 wrapper()
函数取代了。
若是咱们须要用到元函数信息,那怎么保留它呢?这时候能够用内置装饰器@functools.wrap
。
import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print("这是装饰后具备的新输出") func(*args, **kwargs) return wrapper @my_decorator def hello(people_name): print("你好,{}".format(people_name)) print(hello.__name__)
运行下:
hello [Finished in 0.1s]
装饰器除了是函数以外,也能够是类。
可是类做为装饰器的话,须要依赖一个函数__call__()
,当调用这个类的实例时,函数__call__()
就
会被执行。
来改造下以前的例子,把函数装饰器改为类装饰器:
class MyDecorator(): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print("这是装饰后具备的新输出") return self.func(*args, **kwargs) # def my_decorator(func): # def wrapper(): # print("这是装饰后具备的新输出") # func() # return wrapper @MyDecorator def hello(): print("你好,装饰器") hello()
运行:
这是装饰后具备的新输出 你好,装饰器 [Finished in 0.1s]
跟函数装饰器同样,实现同样的功能。
既然装饰器能够加强函数的功能,那若是有多个装饰器,我都想要怎么办?
其实,只要把须要用的装饰器都加上去就行了:
@decorator1 @decorator2 @decorator3 def hello(): ...
可是要注意这里的执行顺序,会从上到下去执行,能够来看下:
def my_decorator(func): def wrapper(): print("这是装饰后具备的新输出") func() return wrapper def my_decorator2(func): def wrapper(): print("这是装饰后具备的新输出2") func() return wrapper def my_decorator3(func): def wrapper(): print("这是装饰后具备的新输出3") func() return wrapper @my_decorator @my_decorator2 @my_decorator3 def hello(): print("你好,装饰器") hello()
运行
这是装饰后具备的新输出 这是装饰后具备的新输出2 这是装饰后具备的新输出3 你好,装饰器 [Finished in 0.1s]
好记性不如烂笔头,写一下理解一下会好不少。