装饰器是Python的一大特点,虽然其他语言也可以通过各种设计模式实现装饰器的功能,但是Python无疑是最简洁的.
Python将一切东西都看做是对象,也包括函数,这说明函数可以像变量那样随意传递.
def hi():
return "hi Python!"
def doSomethingBeforeHi(func):
print("我在 hi() 之前运行")
print(func())
doSomethingBeforeHi(hi)
def a_new_decorator(a_func):
def wrapTheFunction():
print("在函数之前做点事情")
a_func()
print("在函数之后做点事情")
return wrapTheFunction
def a_function_requiring_decoration():
print("我是一个函数")
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
a_function_requiring_decoration()
发现没有,原本a_function_requiring_decoration
只打印一句话,但是经过a_new_decorator
的包装,在a_function_requiring_decoration
的前面和后面分别执行了一些内容.
不仅如此,Python可以以更加简洁的方式执行上述的操作.
@a_new_decorator
def a_function_requiring_decoration():
"""Hey you! Decorate me!"""
print("我是一个函数")
a_function_requiring_decoration()
这用处可大了去了.比如说:
根据业务需求的不同,装饰器的应用场景可以非常丰富.
下面举一个权限检测的例子,定义装饰器:
from functools import wraps
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
在调用装饰器后,使用.__name__
打印函数名字会显示为装饰器的名字,我们希望调用装饰器后还是显示原函数的信息,这里的@wraps(func)
就是将logit
的信息改为func
的信息.
使用装饰器做日志:
@logit
def test1(x):
return x + x
@logit
def test2(x):
return x**2
res = test1(4)
res = test2(4)
上面的装饰器是没有参数的,那么装饰器可以有参数吗?当然可以,之前的@wraps(func)
就是一个带参数的装饰器.
如果需要装饰器传参数可以创建一个包裹函数:
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"
# 现在将日志打到指定的logfile
with open(logfile, 'a') as opened_file:
opened_file.write(log_string + '\n')
return func(*args, **kwargs)
return wrapped_function
return logging_decorator
@logit()
def myfunc1():
pass
myfunc1()
@logit(logfile='func2.log')
def myfunc2():
pass
myfunc2()
类同样可以用作装饰器:
from functools import wraps
class logit(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile
def __call__(self, func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 现在将日志打到指定的文件
with open(self.logfile, 'a') as opened_file:
opened_file.write(log_string + '\n')
# 现在,发送一个通知
self.notify()
return func(*args, **kwargs)
return wrapped_function
def notify(self):
# logit只打日志,不做别的
pass
@logit()
def myfunc1():
pass
可以看到类作为装饰器比函数作为装饰器更加整洁.