装饰器
装饰器实质是一个函数,其作用就是在不改动其它函数代码的情况下,增加一些功能。
如果我们需要打印函数调用前后日志,可以这么做
def log(func):
print('%s is running' % func.__name__)
func()
def bar():
print('bar')
#将bar作为函数log参数传入
>>>log(bar)
bar is running
bar
这样写下来一个函数打印一个函数的日志是没有问题的,但是很多呢?
def log(func)
def wrapper(*args,**kw):
print('%s',%func.__name__)
return func(*args,**kw)
return wrapper
def bar():
print('bar')
bar = log(bar)
bar()
借助decorator的语法糖,一个@就可以解决
def log(func):
def wrapper(*args,**kw):
print('call %s()' %func.__name__)
return func(*args,**kw)
return wrapper
@log #相当于bar = log(bar)
def bar():
print('this is bar')
@log
def bar2():
print('this is bar2')
装饰器在Python使用如此方便都要归因于Python的函数能像普通的对象一样能作为参数传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内。
装饰器是允许带参数的,装饰器的语法可以使我们在调用时,提供其他参数。
import functools def log(level): def decorator(func): #@functools.wraps(func) #这里先注释一下 def wrapper(*args,**kw): print('%s %s()'% (level,func.__name__)) return func(*args,**kw) return wrapper return decorator @log('execute') def bar(): print('bar')
>>>bar()
execute bar()
bar
>>>print(bar.__name__)
wrapper
这里的@log('execute')相当于 bar = log('execute')(bar)
函数对象可以通过__name__属性拿到名称
bar.__name__ 拿到的是wrapper,这是因为bar函数经过装饰器装饰之后,最终返回了wrapper函数
如果不更改bar.__name__的属性,在一些依赖函数签名的代码中就会出错
Python自带的functools.wraps就是将原始函数__name__赋值给wrapper
import functools
def log(level):
def decorator(func):
@functools.wraps(func) #这里删除注释
def wrapper(*args,**kw):
print('%s %s()'% (level,func.__name__))
return func(*args,**kw)
return wrapper
return decorator
@log('execute')
def bar():
print('bar')
>>>bar()
execute bar()
bar
>>>print(bar.__name__)
bar #__name__还原成了bar
类的装饰器
参考链接知乎--Python装饰器