装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
一、函数装饰器---不带参数
不啰嗦,先上“菜”,代码如下
### 函数装饰器
import logging
import sys
# logging.basicConfig函数对日志的输出格式及方式做相关配置
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
## 定义一个输入日志的函数
def use_logging(func):
## wrapper() 函数名可以自定义的,但是返回名一定要与之一致
def wrapper():
logging.info("%s is running" % func.__name__)
logging.info("this is wrapper ")
return func()
return wrapper
### 定义一个方法
@use_logging
def dog():
logging.info("this is a dog method ")
## 调用方法输入
dog()
输出结果是
2019-07-16 15:22:39,325 - decorators_demo_method.py[line:11] - INFO: dog is running
2019-07-16 15:22:39,325 - decorators_demo_method.py[line:12] - INFO: this is wrapper
2019-07-16 15:22:39,325 - decorators_demo_method.py[line:19] - INFO: this is a dog method
从代码可以看出,方法dog()加了装饰器函数 use_logging() 后,首先输出的是 装饰器里的日志,其实也就是先执行装饰器的逻辑,再执行dog()方法本身,
二、函数装饰器--带参数
不啰嗦,先上“菜”,代码如下
输入一个参数,
### 函数装饰器
import logging
import sys
# logging.basicConfig函数对日志的输出格式及方式做相关配置
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
## 定义一个输入日志的函数
def use_logging(dog_type):
def decorator(func):
def wrapper(*args):
### 可以根据参数做业务逻辑,如下
if dog_type == 1:
logging.info("this is a red dog")
elif dog_type == 2:
logging.info("this is a blue dog")
return func(*args)
return wrapper
return decorator
### 定义一个方法
@use_logging(dog_type=1)
def dog(type=2):
logging.info("this is a dog method ")
logging.info("type is %s" % type)
## 调用方法输入
dog()
输出结果如下
2019-07-16 15:57:36,633 - decorators_demo_method.py[line:13] - INFO: this is a red dog
2019-07-16 15:57:36,633 - decorators_demo_method.py[line:23] - INFO: this is a dog method
2019-07-16 15:57:36,633 - decorators_demo_method.py[line:24] - INFO: type is 2
输入多个参数
### 函数装饰器
import logging
import sys
# logging.basicConfig函数对日志的输出格式及方式做相关配置
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
## 定义一个输入日志的函数
def use_logging(dog_type,age):
def decorator(func):
def wrapper(*args):
### 可以根据参数做业务逻辑,如下
if dog_type == 1:
logging.info("this is a red dog")
elif dog_type == 2:
logging.info("this is a blue dog")
if age >= 12:
logging.info("this is a old dog")
elif age < 12:
logging.info("this is a young dog")
return func(*args)
return wrapper
return decorator
### 定义一个方法
@use_logging(dog_type=1,age = 12)
def dog(type=2):
logging.info("this is a dog method ")
logging.info("type is %s" % type)
## 调用方法输入
dog()
对比以上两个,
有参数与无参数比较,最主要区别就是装饰器函数在 wrapper()外多了 个
decorator()函数
三、类装饰器
装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
老套路,直接上“菜”
#定义一个类
class Vet(object):
def __init__(self, func):
self._func = func
def __call__(self):
logging.info ('class Vet decorator is runing')
self._func()
logging.info ('class Vet decorator ending')
### 定义一个方法
@Vet
def dog(type=1):
logging.info("this is a dog method ")
logging.info("type is %s" % type)
## 调用方法输入
dog()
运行结果
2019-07-16 17:35:39,818 - decorators_demo_class.py[line:17] - INFO: class Vet decorator is runing
2019-07-16 17:35:39,818 - decorators_demo_class.py[line:24] - INFO: this is a dog method
2019-07-16 17:35:39,818 - decorators_demo_class.py[line:25] - INFO: type is 1
2019-07-16 17:35:39,818 - decorators_demo_class.py[line:19] - INFO: class Vet decorator ending
总结下,python 的装饰器达其实就是把做同样的事情代码封装下,以“@函数”或 “@类名”的方式装饰函数,降低 代码的耦合,提高代码的共同性,特别适合 日志、性能测试、事务处理、缓存、权限等场景,说到这里,熟悉java开发童鞋,一定想到了,这与spring框架的aop思想,可以说是异曲同工!!
本文只是将装饰器基本用法,还有装饰器功能,比如 装饰器顺序,缓存、权限等真实场景的应用,后续会补上!!