装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。下面是一个简单的修饰器例子。
# coding = utf-8
import time
from functools import wraps #知识点3注释
def decorator_logging(func):
@wraps(func) #知识点3注释
def wraper(*args, **kwargs):
print("%s is running now." % (func.__name__))
return func(*args, **kwargs)
return wraper
def timec(func):
@wraps(func) #知识点3注释
def warper(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
end = time.time()
print("%s function cost time: %f s" % (func.__name__, (end - start)))
return warper
@timec
@decorator_logging
def hi(name="Devin"):
print("hello, " + name)
if __name__ = '__main__':
hi("Cat")
记录一下上面这段代码,我觉得比较重要的知识点:
- 这里面有两个修饰器
decorator_logging
和timec
。当有多个修饰器时,是先从内侧向外侧按顺序运行的。 - 装饰器在 Python 使用如此方便都要归因于 Python 的函数能像普通的对象一样能作为参数传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内。这个功能是通过参数
*args、**kwargs
实现的。 - 使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的
docstring
、__name__
、参数列表等等,我们是通过第三方模块functools
中的wraps
修饰器来保存传递进装饰器中的func
函数信息。如上段代码中知识点3的标记注释。
还有一个比较有意思的是带参数的装饰器:
def use_logging(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warn("%s is running" % func.__name__)
elif level == "info":
logging.info("%s is running" % func.__name__)
return func(*args)
return wrapper
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
类装饰器
没错,装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__
方法,当使用@形式将装饰器附加到函数上时,就会调用此方法。
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
print ('class decorator runing')
self._func()
print ('class decorator ending')
@Foo
def bar():
print ('bar')
bar()
functools.wraps
参考网页资料:
网友评论