装饰器的作用:
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。
使用闭包来调用一个函数:
def funcA(func):
def funcB():
print("Before call function")
func()
print("After call function")
return funcB
def funcD():
print("This is funcD")
if __name__ == '__main__':
t=funcA(funcD)
t()
# Before call function
# This is funcD
# After call function
使用装饰器代替:
def funcA(func):
def funcB():
print("Before call function")
func()
print("After call function")
return funcB
@funcA
def funcD():
print("This is funcD")
if __name__ == '__main__':
funcD()
# Before call function
# This is funcD
# After call function
使用装饰器后再次调用funcA,会发现:
def funcA(func):
def funcB():
print("Before call function")
func()
print("After call function")
return funcB
@funcA
def funcD():
print("This is funcD")
if __name__ == '__main__':
t=funcA(funcD)
t()
#
# Before call function
# Before call function
# This is funcD
# After call function
# After call function
就相当于加了两层修饰器:
def funcA(func):
def funcB():
print("Before call function")
func()
print("After call function")
return funcB
@funcA
@funcA
def funcD():
print("This is funcD")
if __name__ == '__main__':
funcD()
#
# Before call function
# Before call function
# This is funcD
# After call function
# After call function
使用装饰器后,打印一下函数名称,发现在函数体外打印并不是funcD,这是因为funcB重写了我们函数的名字和注释文档:
def funcA(func):
def funcB():
print("Before call function")
print("inside,func_name:",func.__name__)
func()
print("After call function")
return funcB
@funcA
def funcD():
print("This is funcD")
if __name__ == '__main__':
funcD()
print("outside,func_name:",funcD.__name__)
# Before call function
# inside,func_name: funcD
# This is funcD
# After call function
# outside,func_name: funcB
解决方法:使用functools.wraps
from functools import wraps
def funcA(func):
@wraps(func)
def funcB():
print("Before call function")
print("inside,func_name:",func.__name__)
func()
print("After call function")
return funcB
@funcA
def funcD():
print("This is funcD")
if __name__ == '__main__':
funcD()
print("outside,func_name:",funcD.__name__)
# Before call function
# inside,func_name: funcD
# This is funcD
# After call function
# outside,func_name: funcD
函数中有参数:
from functools import wraps
def funcA(func):
@wraps(func)
def funcB(s):
print(func.__name__," was called")
return func(s)
return funcB
@funcA
def funcD(s1):
print(s1)
if __name__ == '__main__':
funcD("this is func")
# funcD was called
# this is func
但是如果另一个函数有多个参数怎么办,幸好python提供了不定参数个数的解决办法:
from functools import wraps
def funcA(func):
@wraps(func)
def funcB(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
return func(*args, **kwargs)
return funcB
@funcA
def funcD(s1,s2):
print(s1,s2)
@funcA
def funcE(s1):
print(s1)
if __name__ == '__main__':
funcD('hello','world')
funcE("hello,python")
# funcD was called
# hello world
# funcE was called
# hello,python
带参数的装饰器
from functools import wraps
def funcA(level):
def wrapper(func):
def inner_wrapper(*args,**kwargs):
print("level:{},func_name:{}".format(level,func.__name__))
return func(*args,**kwargs)
return inner_wrapper
return wrapper
@funcA(level='debug')
def funcB(s1):
print(s1)
@funcA(level='error')
def funcD(s1,s2):
print(s1,s2)
if __name__ == '__main__':
funcB("this is funcB")
funcD('hello','world')
#
# level:debug,func_name:funcB
# this is funcB
# level:error,func_name:funcD
# hello world
基于类实现的装饰器
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。
在Python中一般callable对象都是函数,但也有例外。只要某个对象重载了__call__()方法,那么这个对象就是callable的。
class logging(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print ("[DEBUG]: enter function {func}()".format(
func=self.func.__name__))
return self.func(*args, **kwargs)
@logging
def say(something):
print ("say {}!".format(something))
if __name__ == '__main__':
say('hello')
# [DEBUG]: enter function say()
# say hello!
使用场景
它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。有了装饰器,可以抽离出大量与函数功能本身无关的雷同代码并继续重用
日志:
from functools import wraps
def logit(logfile='out.log'):
def logging_decorator(func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile,并写入内容
with open(logfile, 'a') as opened_file:
# 现在将日志打到指定的logfile
opened_file.write(log_string + '\n')
return func(*args, **kwargs)
return wrapped_function
return logging_decorator
@logit(logfile='func.log')
def func1():
print("this is func1")
@logit(logfile='func.log')
def func2():
print("this is func2")
if __name__ == '__main__':
func1()
func2()
# func1 was called
# this is func1
# func2 was called
# this is func2
捕获异常:
from functools import wraps
import sys
def decration_func(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
func(*args, **kwargs)
except Exception as err:
print(err)
sys.exit(1)
return wrapper
@decration_func
def testuse():
a, b = 1, 0
print(a / b)
if __name__ == '__main__':
testuse()