装饰器的实现
任何可调用对象(任何实现了call方法的对象都是可调用的)都可以用作装饰器,他们返回的对象往往是实现了自己的call方法的更复杂的类的实例
装饰器使用语法
装饰器本质也是一个函数,其返回值就是一个函数对象
使用方法:
1.先定义一个装饰函数(或者类或者偏函数实现)
2.再定义业务函数或者类
3.将业务函数作为参数传入装饰函数
装饰器通用模板
函数装饰器通用模板:
def mydecorator(function):
def wrapped(*args, **kwargs):
# 在调用原始函数之前,做点什么
result = function(*args, **kwargs)
# 在函数调用之后,做点什么
# 并返回结果
return result
# 返回 wrapper 作为装饰函数
return wrapped
类装饰器通用模板:如果装饰器需要复杂的参数化或者依赖于特定状态,使用用户自定义类可能更好
class DecoratorAsClass:
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
# 调用原始函数前,做点什么
result = self.function(*args, **kwargs)
# 调用函数之后,做点什么
# 并返回结果
return result
装饰器简单应用
装饰器应用:
- 数据验证
- 日志
- 缓存
- 监控
- 调试
- 业务规则
- 加密
1.日志打印器
2.时间计时器
# 日志打印器
def logger(func):
def wrapper(*args, **kwargs):
print("我准备开始计算:{}函数了".format(func.__name__))
func(*args, **kwargs)
print("我已执行完函数了!!!")
return wrapper
@logger
def add(x, y):
print('{} + {} = {}'.format(x, y, x+y))
add(1, 2)
----------------
#我准备开始计算:add函数了
#1 + 2 = 3
#我已执行完函数了!!!
# 时间计时器
def timer(func):
def wrapper(*args, **kwargs):
t1 = time.time()
func(*args, **kwargs)
t2 = time.time()
result_time = t2 - t1
print("执行函数一共花了{}".format(result_time))
return wrapper
import time
@timer
def want_sleep(sleep_time):
time.sleep(sleep_time)
want_sleep(10)
----------------------
#执行函数一共花了10.008701086044312
带参数的函数装饰器
use_logging 是一个带参数的装饰器,我们可以理解为它是一个含有参数的闭包,当我们使用@use_logging(level=’warn’),
将业务函数foo1作为参数传入,传递到装饰器中
# 带参数的装饰器
def use_logging(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == 'warn':
print("warning !!!!!,{} is running".format(func.__name__))
if level == 'info':
print("{} is running".format(func.__name__))
return func(*args, **kwargs)
return wrapper
return decorator
@use_logging(level='warn')
def foo1(name='foo'):
print("i am {}".format(name))
@use_logging(level='info')
def foo2(name='FOO'):
print("i am {}".format(name))
foo1()
foo2()
----------
#warning !!!!!,foo1 is running
#i am foo
#foo2 is running
#i am FOO
不带参数的类装饰器
实现类装饰器,必须实现__call,和 _init两个内置函数,
init:用于接收被装饰的函数,
call:用于实现装饰逻辑
class logger:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("[INFO]:the function {func}() is running".format(func=self.func.__name__))
return self.func(*args, **kwargs)
@logger
def say(name):
print("say {}!".format(name))
say("zaygee")
---------------------
#[INFO]:the function say() is running
#say zaygee!
带参数的类装饰器
实现带参数的类装饰器,必须实现__call,和 _init两个内置函数,
init:用于接收传入参数
call:用于接收被装饰函数+实现装饰逻辑
# 带参数的类装饰器
class loggering(object):
def __init__(self, level='INFO'):
"""接收传入参数"""
self.level = level
def __call__(self, func):
"""接收函数并返回函数"""
def warpper(*args, **kwargs):
print("[{level}]: the function {func}() is runnig".format(level=self.level, func=func.__name__))
func(*args, **kwargs)
return warpper
@loggering(level='WARNNING')
def say(name):
print("say {name}".format(name=name))
say("i am zaygee")
------------------------
#[WARNNING]: the function say() is runnig
#say i am zaygee
# 封装http请求日志打印
class Logger:
"""带参数的类装饰器:http日志输出"""
def __init__(self, method):
self.method = method
def __call__(self, func):
def logging(*args, **kwargs):
logger.info(f"用例开始执行{func.__name__}")
res = func(*args, **kwargs)
if self.method.lower() == 'post':
logger.info(f'请求URl:{res.url}')
else:
logger.info('不是post请求')
logger.info(f"用例结束执行{func.__name__}")
return logging
class BaseRequest:
headers = {'Accept': 'application/json, text/javascript, */*; q=0.01',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/81.0.4044.122 Safari/537.36',
'Connection': 'keep-alive',
'Content-Type': 'application/json'}
def __init__(self):
self.res = requests.session()
self.res.headers = self.headers
@Logger(method='post')
def base_post(self, url, data=None, json=None, **kwargs):
res = self.res.post(url=url, data=data, json=json, verify=True, **kwargs)
return res
2021-07-11 22:51:55.818 | INFO | __main__:case_logging:105 - 用例开始执行
2021-07-11 22:51:55.818 | INFO | __main__:case_logging:107 - 1*4 = 4
2021-07-11 22:51:55.818 | INFO | __main__:case_logging:108 - 用例执行结束
2021-07-11 22:51:55.819 | INFO | __main__:logging:229 - 用例开始执行base_post
2021-07-11 22:51:56.058 | INFO | __main__:logging:232 - 请求URl:https://web/accounts/login
2021-07-11 22:51:56.059 | INFO | __main__:logging:235 - 用例结束执行base_post
wraps装饰器
@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
装饰器极大的复用了代码,但是唯一的缺点就是原函数的元信息不见了 ,wraps
本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了
# functions.wraps
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("calling decorated function")
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""
Dcostring
"""
print("called decorated function")
example()
print(example.__name__)
print(example.__doc__)
-------------
#calling decorated function
#called decorated function
#example
# Dcostring
内置装饰器property
通常存在于类中,可以将函数定义成一个属性,属性的值就是该函数的返回,
当我们需要对属性做合法性的校验,可以考虑使用proprety装饰器
class C:
def __init__(self):
self._x = None
@property
def x(self):
"""i am the 'x' property"""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
c = C()
c.x = 10 # 设置属性
print(c.x) # 查看属性
del c.x # 删除属性
-------------
#10
例子:校验手机号的合法性
class Phone:
def __init__(self):
self._x = None
@property
def x(self):
"""i am the 'x' property"""
return self._x
@x.setter
def x(self, value):
if not isinstance(value, int):
# raise ValueError("请输入合法手机号")
print("请输入合法手机号")
else:
self._x = value
print("输入的手机:{}是合法的".format(self._x))
@x.deleter
def x(self):
del self._x
sj = Phone()
sj.x = 'dfdfd'
del sj.x
sj.x = 13838839333
----------------
#请输入合法手机号
#输入的手机:13838839333是合法的
多装饰器装饰的执行顺序
执行顺序为:至上而下,再至下而上 执行
from logzero import logger
import time
# here put the import lib
def my_decorator(func):
def wrapper(*args, **kwargs):
logger.info(f"调用业务代码前执行")
result = func(*args, **kwargs)
logger.info(f"调用业务代码后执行")
return result
return wrapper
def decorator_time(func):
def wrapper(*args, **kwargs):
start = time.time()
logger.info(f"开始执行: {start}")
result = func(*args, **kwargs)
logger.info(f"执行代码耗时: {time.time()-start}")
return result
return wrapper
def decorator_with_params(params):
def decorator(func):
def wrapper(*args, **kwargs):
if params != 1:
logger.info("params=1")
else:
logger.info("params is not eq 1")
return func(*args, **kwargs)
logger.info("带参数decorator执行完毕")
return wrapper
return decorator
@decorator_time
@my_decorator
@decorator_with_params(1)
def test_decorator():
logger.info("执行业务代码")
if __name__ == "__main__":
test_decorator()
"""
[I 220112 23:30:23 test1:23] 开始执行: 1642001423.521113
[I 220112 23:30:23 test1:13] 调用业务代码前执行
[I 220112 23:30:23 test1:35] params is not eq 1
[I 220112 23:30:23 test1:46] 执行业务代码
[I 220112 23:30:23 test1:15] 调用业务代码后执行
[I 220112 23:30:23 test1:25] 执行代码耗时: 0.00032520294189453125
"""
使用decorator轻松实现装饰器
from decorator import decorator
# @decorator
# def decoration(func, *args, **kwargs):
# """decorator装饰 生成一个装饰器函数"""
# print("Ready to run func " + func.__name__)
# func(*args, **kwargs)
# print("successful to run func " + func.__name__)
# 等同于如下实现
def decoration(func, *args, **kwargs):
def wrapper(*args, **kwargs):
print("Ready to run func " + func.__name__)
func(*args, **kwargs)
print("successful to run func " + func.__name__)
return wrapper
@decoration
def demo():
print("run the demo task")
demo()
print(demo.__name__) # wrapper
@decorator
def decoration_with_params(func, values=1, *args, **kwargs):
results = func(*args, **kwargs)
if values == 1:
print('values = 1')
return results
else:
print('values ! = 1')
return '3'
@decoration_with_params(values=1)
def demo_2():
print("run the demo 2 task")
return 1
print(demo_2())
print(demo_2.__name__) # demo_2
参考自:https://zhuanlan.zhihu.com/p/65968462、https://foofish.net/decorator.html