装饰器概念
装饰器是python中的一个高阶概念,它是可调用的对象,其参数是另外一个函数,装饰器可能会处理被装饰的函数然后把它返回,或者将其替换成另外一个函数或者可调用的对象。
装饰器本质上是一个python函数或者类,它可以让其他函数或者类,在不进行任何代码修改的前提下增加额外的功能,装饰器的返回值也是一个函数或者类对象。
作用
装饰器的强大在于能够在不改变原有业务逻辑的情况下对代码进行扩展,常见的常见有:权限校验、用户认证、日志记录、性能测试、事务处理、缓存等。有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并重用,概括的讲,就是装饰器可以为已经存在的对象添加额外的功能。
案例
谈装饰器前,需要明白一件事,Python中的函数和Java、C++不同,python中的函数可以像普通变量一样当作参数,传递给另外一个函数,例如:
def foo():
print("foo")
def bar(func):
func()
bar(foo)
案例1
先看一个简单的例子:
def foo():
print("I am foo")
现在有一需求,可以记录下函数的执行日志,于是在代码中添加日志代码:
from colorlog import logging
def foo():
print("I am foo")
logging.warning.("foo is running")
如果函数bar1, bar2,也有类似的需求,怎么做?在每一个函数中都写一个logging吗?这样就会造成大量的雷同代码,为了减少大量重复代码,我们可以这样做,重新定一个一个新的函数:专门处理日志,日志处理完后在执行真正的业务代码
def use_logging(func):
logging.warning(f"{func.__name__} is running"
func()
def foo():
print("I am foo")
use_logging(foo)
这样做逻辑上没有问题,功能实现了,但是在调用的时候不是调用的真正的业务逻辑函数foo,而是调用的use_logging函数,这就破坏了原有的代码结构,现在我们不得不每次都把foo函数当作参数传给use_logging函数,有没有其他的方法?答案就是使用装饰器
案例2:简单的装饰器
def use_logging(func):
def wrapper():
logging.warning(f"{func.__name__} is running")
return func()
return wrapper
@use_logging
def foo():
print("I am foo")
# 因为装饰器 use_logging(foo) 返回的时函数对象 wrapper,这条语句相当于 foo = wrapper
foo = use_logging(foo)
# 执行foo()就相当于执行 wrapper()
foo()
use_logging就是一个装饰器,它是一个普通的函数,它把执行真正业务逻辑的函数foo包裹在其中,看起来就像foo函数被use_logging装饰了一样,use_wrapper返回的是wrapper函数
案例3 @ 语法糖
def use_logging(func):
def wrapper():
logging.warning(f"{func.__name__} is running")
return func()
return wrapper
@use_logging
def foo():
print("I am foo")
foo()
案例4 带多个参数
def use_logging(func):
def wrapper(*args, **kwargs):
logging.warning(f"{func.__name__} is running")
return func(*args, **kwargs)
return wrapper
@use_logging
def foo(name):
print("I am {name}")
foo("zk")
案例5 带参数装饰器
装饰器还有更大的灵活性,例如带参数的装饰器,上面的装饰器的调用,装饰器接收的唯一参数就是foo函数,装饰器的语法允许我们在调用是提供其他参数,比如@decorate(a),这样就为装饰器的编写和使用提供了更大的灵活性,例如,我们在装饰器中指定日志的等级,不同业务逻辑的函数可能需要不同级别的日志。
def use_logging(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warning":
logging.warning(f"{func.__name__} is running")
elif level == "error":
logging.error(f"{func.__name__} is running")
return func(*args, **kwargs)
return wrapper
return decorator
@use_logging(level="warning")
def foo(name):
print(f"I am {name}")
foo("zk")
上面的use_logging是带参数的装饰器,它实际上是对原有装饰器的一个函数封装,并返回一个装饰器,
类装饰器
装饰器不仅可以是函数,而且还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚,封装性等优点,使用类封装器主要靠类的__call__方法,当使用@形式将装饰器附加到函数上时,就会调用此方法。
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
print("class decorator running")
self._func()
print("class decorator ending")
@Foo
def bar():
print("bar")
bar()
参考
https://foofish.net/python-decorator.html