知识点:python中函数也是对象,可以传递和当做参数的。
装饰器可看成是一个复杂语法的缩写:
@a
def b
...
等价于
b=a(b)
1.最基本的装饰器:参数为函数,返回函数的函数,可作为修饰器
def decorator(in_func):#函数做参数
def out_func(a,b,c):
print("输出点啥")
return in_func(a,b,c)
return out_func#返回函数,参数需要与被装饰函数(use_func)参数一致,或者用*args,**kwargs
#使用
@decorator
def use_func(a,b,c):
...
#等价于
use_func=decorator(use_func)
问题1:名称信息丢失
print(use_func.__name__)#被装饰函数名称
# Output: wrapTheFunction
解决方法:
from functools import wraps
def decorator(in_func):
@wraps(in_func)
...
return out_func#返回函数
print(use_func.__name__)#被装饰函数名称
# Output: use_func
注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
问题2:use_func的参数如何获得的
use_func的参数是如何获得的:
我们可以在定义 out_func 函数的时候指定参数:
def out_func(a,b,c):
logging.warn("%s is running" % in_func.__name__)
return in_func(a,b,c)
return out_func
这样 use_func 函数定义的参数就可以定义在 out_func函数中。use_func如果有很多个参数。当装饰器不知道 use_func 到底有多少个参数时,我们可以用 *args 来代替:
def out_func(*args):
logging.warn("%s is running" % in_func.__name__)
return in_func(*args)
return out_func
对于use_func 函数还定义了的关键字参数比如:
def use_func(name, age=None, height=None):
...
这时,可以把 out_func 函数指定关键字函数:
def out_func(*args, **kwargs):
# args是一个数组,kwargs一个字典
logging.warn("%s is running" % in_func.__name__)
return in_func(*args, **kwargs)
return out_func
2.带参数的装饰器:
例1:
装饰器还有更大的灵活性,例如带参数的装饰器,在上面的装饰器调用中,该装饰器接收唯一的参数就是执行业务的函数 use_func 。装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(a)
。这样,就为装饰器的编写和使用提供了更大的灵活性。比如,我们可以在装饰器中指定日志的等级,因为不同业务函数可能需要的日志级别是不一样的。
def use_logging(level):
def decorator(in_func):
def out_func(*args, **kwargs):
if level == "warn":
logging.warn("%s is running" % in_func.__name__)
elif level == "info":
logging.info("%s is running" % in_func.__name__)
return in_func(*args)
return out_func
return decorator
@use_logging(level="warn")
def foo(name='foo'):
print("i am %s" % name)
foo()
上面的 use_logging 是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我 们使用@use_logging(level="warn")
调用的时候,Python 能够发现这一层的封装,并把参数传递到装饰器的环境中。
@use_logging(level="warn")
等价于@decorator
例2:
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()
def myfunc1():
pass
myfunc1()
# Output: myfunc1 was called
# 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
@logit(logfile='func2.log')
def myfunc2():
pass
myfunc2()
# Output: myfunc2 was called
# 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串
3.装饰器类:
装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__
方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
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)
# 打开logfile并写入
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
现在,我们给 logit 创建子类,来添加 email 的功能(虽然 email 这个话题不会在这里展开)。
class email_logit(logit):
'''
一个logit的实现版本,可以在函数调用时发送email给管理员
'''
def __init__(self, email='admin@myproject.com', *args, **kwargs):
self.email = email
super(email_logit, self).__init__(*args, **kwargs)
def notify(self):
# 发送一封email到self.email
# 这里就不做实现了
pass
从现在起,@email_logit 将会和 @logit 产生同样的效果,但是在打日志的基础上,还会多发送一封邮件给管理员。
参考自:
1:https://www.runoob.com/w3cnote/python-func-decorators.html\