装饰器本质上还是一个函数,它可以让已有的函数不做任何改动的情况下增加功能。
接下来我们使用一些例子来具体说明装饰器的作用:
如果我们不使用装饰器,我们通常会这样来实现在函数执行前插入日志:
def foo(): print('i am foo') def foo(): print('foo is running') print('i am foo')虽然这样写是满足了需求,但是改动了原有的代码,如果有其他的函数也需要插入日志的话,就需要改写所有的函数,这样不能复用代码。
我们可以进行如下改写:
import logging def use_log(func): logging.warning("%s is running" % func.__name__) func() def bar(): print('i am bar') use_log(bar) #将函数作为参数传入 -------------运行结果如下-------------- WARNING:root:bar is running i am bar这样写的确可以复用插入的日志,缺点就是显式的封装原来的函数,我们希望能隐式的做这件事。
我们可以用装饰器来写:
import logging def use_log(func): def wrapper(*args, **kwargs): logging.warning('%s is running' % func.__name__) return func(*args, **kwargs) return wrapper def bar(): print('I am bar') bar = use_log(bar) bar() ------------结果如下------------ WARNING:root:bar is running I am bar其中,use_log函数就是装饰器,它把我们真正想要执行的函数bar()封装在里面,返回一个封装了加入代码的新函数,看起来就像是bar()被装饰了一样。
但是这样写还是不够隐式,我们可以通过@语法糖来起到bar = use_log(bar)的作用。
import logging def use_log(func): def wrapper(*args, **kwargs): logging.warning('%s is running' % func.__name__) return func(*args, **kwargs) return wrapper @use_log def bar(): print('I am bar') @use_log def haha(): print('I am haha') bar() haha() ------------结果如下------------ WARNING:root:bar is running I am bar WARNING:root:haha is running I am haha这样子看起来就非常简洁,而且代码很容易复用。可以看成是一种智能的高级封装。
另外,装饰器也是可以带参数的,这使得装饰器提供了非常大的灵活性。
import logging def use_log(level): def decorator(func): def wrapper(*args, **kwargs): if level == "warn": logging.warning("%s is running" % func.__name__) return func(*args) return wrapper return decorator @use_log(level="warn") def foo(name='foo'): print("i am %s" % name) foo() -----------------结果如下--------------- WARNING:root:foo is running i am foo实际上是对装饰器的一个函数封装,并返回一个装饰器。这里涉及到作用域的概念,之前有一篇博客提到过。可以把它看成一个带参数的闭包。当使用 @use_log(level=‘warn’) 时,会将 level 的值传给装饰器的环境中。它的效果相当于 use_log(level=‘warn’)(foo) ,也就是一个三层的调用。
装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法。
class Foo(object): def __init__(self, func): self._func = func def __call__(self): print('class decorator running') self._func() print('class decorator ending') @Foo def bar(): print ('bar') bar() -----------结果如下-------------- class decorator running bar class decorator ending每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它变得更厚更长,这样一来,它不仅有遮羞功能,还能提供保暖,不过有个问题,这个内裤被我们改造成了长裤后,虽然还有遮羞功能,但本质上它不再是一条真正的内裤了。于是聪明的人们发明长裤,在不影响内裤的前提下,直接把长裤套在了内裤外面,这样内裤还是内裤,有了长裤后宝宝再也不冷了。装饰器就像我们这里说的长裤,在不影响内裤作用的前提下,给我们的身子提供了保暖的功效。