赞
踩
知识点: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\
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。