一、装饰器简述
? 在不改变原有函数的调用方式的前提下给函数增加新的功能。它经常用于有切面需求的场景,比如:插入日志、性能测试、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。 概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
二、装饰器分类
2.1 无参装饰器
import time
def decorator(func):
def inner(*args, **kwargs):
print(f"不定长位置参数:{args}")
print(f"不定长关键字参数:{kwargs}")
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f"函数运行时间:{end - start}s")
return result
return inner
'''
被装饰函数在未调用之前,已经被装饰器函数所装饰
'''
@decorator
def test():
'''
被修饰的函数不带参数且无返回值
:return:
'''
print("被修饰的函数不带参数且无返回值!!!")
@decorator
def func(a, b, c=10, d=20):
'''
被修饰的函数带参数
:param a: 位置参数
:param b: 位置参数
:param c: 关键字参数
:param d: 关键字参数
:return:
'''
print("被修饰的函数带参数")
print(f"位置参数:{a} {b}")
print(f"关键字参数:{c} {d}")
@decorator
def bar(a, c=9):
print("被修饰的函数带参数且有返回值")
return a + c
if __name__ == '__main__':
test()
print("=" * 50)
func(5, "李白", c=90, d=100)
print("=" * 50)
func(5, "李白")
print("=" * 50)
print(bar(100))
2.2 有参装饰器
def level(num):
def decorator(func):
def inner(*args, **kwargs):
if num == 1:
print("权限1验证成功...")
print("获得登录index页面的权限...")
return func(*args, **kwargs)
elif num == 2:
print("权限2验证成功...")
print("获得登录商品详情页的权限...")
return func(*args, **kwargs)
else:
raise ValueError("请输入正确的权限验证级别参数:1 or 2")
return inner
return decorator
@level(2)
def login(username, pwd):
if username == "admin" and pwd == "123456":
return "login success"
return "login failure"
if __name__ == '__main__':
print(login("admin", "123456"))
2.3 多层装饰器
def permissions(func):
'''权限验证器'''
print("权限验证装饰器...")
def inner(*args, **kwargs):
username, _ = args
if username == "admin":
print("权限验证成功...")
return func(*args, **kwargs)
else:
raise ValueError(f"{username}该用户不具备权限")
return inner
def identity(func):
'''身份验证器'''
print("身份验证装饰器...")
def inner(*args, **kwargs):
username, pwd = args
if username in ("admin", "jess", "lucy") and pwd == "123":
print("identity success...")
return func(*args, **kwargs)
raise ValueError("用户名密码错误,身份验证失败...")
return inner
'''
装饰顺序由里到外,调用顺序是由外及里
'''
@identity
@permissions
def user(username, pwd):
print(username, pwd)
return "login success"
if __name__ == '__main__':
print(user("admin", "123"))
2.4 类装饰器
class Decorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("这里是装饰器添加的功能...")
print(args)
print(kwargs)
a, b = args
if isinstance(a, int) and isinstance(b, int):
return self.func(*args, **kwargs)
else:
raise ValueError("值错误...")
@Decorator
def add(a, b):
return a + b
if __name__ == '__main__':
print(add(10, 20))
2.5 被装饰的对象是类
def decorator(func):
def inner(*args, **kwargs):
print("装饰器装饰类...")
return func(*args, **kwargs)
return inner
@decorator
class MyClass:
def __init__(self):
print("__init__")
def eat(self, food):
print(f"睡醒了吃{food}大餐...")
if __name__ == '__main__':
a = MyClass()
a.eat("大鱼大肉")
?
三、装饰器应用场景
2.1 插入日志
? 正式项目的日志模块是结合loggin和日志配置文件生成相应日志的。使用装饰器生成日志仅供参考,不推荐使用。
import logging
def logged(func):
def inner(*args, **kwargs):
logging.basicConfig(level=logging.DEBUG
, filename="demo.log"
, filemode="w"
,format="%(asctime)s - %(name)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s"
, datefmt="%Y-%m-%d %H:%M:%S"
)
try:
result = func(*args, **kwargs)
except Exception as error:
logging.exception(error)
else:
return result
return inner
@logged
def foo(a, b):
return a + b
if __name__ == '__main__':
print(foo(10, "abc"))
2.2 性能测试
import time
import random
def decorator(func):
def inner(*args, **kwargs):
print("装饰器性能测试...")
s_time = time.perf_counter()
res = func(*args, **kwargs)
e_time = time.perf_counter()
print(f"程序运行时间:{e_time - s_time}s")
return res
return inner
@decorator
def loop(num):
s = 0
for i in range(num):
random_num = random.randint(0, 10000000)
s += random_num
return s
if __name__ == '__main__':
print(loop(100))
print("=" * 50)
print(loop(1000))
print("=" * 50)
print(loop(10000))
print("=" * 50)
print(loop(100000))
print("=" * 50)
print(loop(10000000))
print("=" * 50)
print(loop(100000000))
2.3 缓存
? 在web开发中,缓存是常用来提高服务器的响应速度以及减少数据库压力的用力手段。在处理缓存时,有三个重要的步骤生成缓存键,存入缓存和获取缓存数据。对不同的缓存软件(Redis,Memcached等)操作基本相同,只是在具体的存储获取环节存在差异。
? 缓存操作主要有两种类型。缓存如浏览器缓存,服务器缓存,代理缓存,硬件缓存工作原理的读写缓存。当处理缓存时,我们总是有大量的内存需要花费大量的时间来读写数据库、硬盘。 缓存则能帮我们加快这些任务。
参考文档:https://cloud.tencent.com/developer/article/1665019
2.3.1 双端队列实现 LRU 机制
from collections import deque
from typing import Any
class LRUCache:
def __init__(self, cache_size: int):
"""
:param cache_size: LRU 队列的大小,超过当前大小时,最近最不常使用的元素将过期
"""
self.cache_size = cache_size
self.queue = deque()
self.hash_map = dict()
def is_queue_full(self):
return len(self.queue) == self.cache_size
def set(self, key: str, value: Any):
if key not in self.hash_map:
if self.is_queue_full():
pop_key = self.queue.pop()
self.hash_map.pop(pop_key)
self.queue.appendleft(key)
self.hash_map[key] = value
else:
self.queue.appendleft(key)
self.hash_map[key] = value
def get(self, key: str):
if key not in self.hash_map:
return -1
else:
self.queue.remove(key)
self.queue.appendleft(key)
return self.hash_map[key]
2.3.2 内置缓存模块
? 在软件或系统开发中,缓存总是必不可少,这是一种空间换时间的技术,通过将频繁访问的数据缓存起来,下一次访问时就可以快速获得期望的结果。一个缓存系统,关键核心的指标就是缓存命中率。LRU是一种常用的缓存算法,即最近最少使用,如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小, LRU算法选择将最近最少使用的数据淘汰,保留那些经常被命中的数据。
import time
from functools import lru_cache
class Model:
@lru_cache(maxsize=10)
def calculate(self, number):
print(f'calculate({number}) is running,', end=' ')
print('sleep 3s ')
time.sleep(3)
return number * 3
if __name__ == '__main__':
model = Model()
for i in range(5):
print(model.calculate(i))
for i in range(5):
print(model.calculate(i))
2.4 权限校验
user_list = [
{'name': 'ad1', 'passwd': '123'},
{'name': 'ad2', 'passwd': '123'},
{'name': 'ad3', 'passwd': '123'},
{'name': 'ad4', 'passwd': '123'}
]
def auth(func):
def wrapper(*args, **kwargs):
param = args[0]
if param != "index.html":
username = input("请输入用户名:")
pwd = input("请输入用户密码:")
for user in user_list:
if username == user['name'] and pwd == user['passwd']:
if param in ("shop.html", "root"):
print("登录成功!!!")
return func(*args, **kwargs)
raise ValueError("传入参数错误...")
raise ValueError("输入的用户名和密码不存在,请进入注册页面注册用户...")
return func(*args, **kwargs)
return wrapper
@auth
def index(url):
print(f"主页url:{url} 欢迎来到主页...")
@auth
def home(name):
print("欢迎回家:%s" % name)
@auth
def shop(url):
print(f'购物页面路由信息:{url},购物车里有奶茶, 妹妹, 娃娃')
if __name__ == '__main__':
index('index.html')
home('root')
shop('shop.html')
shop('shop1.html')
2.5 框架路由传参
URL_CONF = {}
def route(url):
def set_func(func):
URL_CONF[url] = func
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner
return set_func
@route('index.py')
def index():
return "欢迎访问首页页面"
@route("register.py")
def register():
return "欢迎来到注册页面"
def application(file_name):
func = URL_CONF[file_name]
return func
if __name__ == '__main__':
app1 = application("index.py")
app2 = application("register.py")
print(app1())
print(app2())
|