浅谈装饰器

一、装饰器简述

​ 在不改变原有函数的调用方式的前提下给函数增加新的功能。它经常用于有切面需求的场景,比如:插入日志、性能测试、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。 概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

二、装饰器分类

2.1 无参装饰器

# -*- coding: utf-8 -*-
# @Time     : 2021/11/25 22:01
# @Author   : Aries
# @File     : 无参装饰器.py
# @Describe : 无参装饰器
# @Software : PyCharm

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 有参装饰器

# -*- coding: utf-8 -*-
# @Time     : 2021/11/26 10:41
# @Author   : Aries
# @File     : 有参装饰器.py
# @Describe : 
# @Software : PyCharm

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 多层装饰器

# -*- coding: utf-8 -*-
# @Time     : 2021/11/26 11:05
# @Author   : Aries
# @File     : 多层装饰器.py
# @Describe : 
# @Software : PyCharm

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"))
    #print(user("jess", "123"))

2.4 类装饰器

# -*- coding: utf-8 -*-
# @Time     : 2021/11/26 11:37
# @Author   : Aries
# @File     : 类装饰器.py
# @Describe : 
# @Software : PyCharm

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))

    #print(add(10, "abcd"))

2.5 被装饰的对象是类

# -*- coding: utf-8 -*-
# @Time     : 2021/11/26 11:05
# @Author   : Aries
# @File     : 装饰器装饰类.py
# @Describe : 
# @Software : PyCharm

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和日志配置文件生成相应日志的。使用装饰器生成日志仅供参考,不推荐使用。

# -*- coding: utf-8 -*-
# @Time     : 2021/11/26 15:50
# @Author   : wljess
# @File     : 日志处理.py
# @Describe : 
# @Software : PyCharm

import logging


def logged(func):
    def inner(*args, **kwargs):
        logging.basicConfig(level=logging.DEBUG  # 设置日志输出格式
                            , filename="demo.log"  # log日志输出的文件位置和文件名
                            , filemode="w"  # 文件的写入格式,w为重新写入文件,默认是追加
                            ,format="%(asctime)s - %(name)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s"
                            # 日志输出的格式
                            # -8表示占位符,让输出左对齐,输出长度都为8位
                            , 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 性能测试

# -*- coding: utf-8 -*-
# @Time     : 2021/11/26 15:33
# @Author   : wljess
# @File     : 性能测试.py
# @Describe : 
# @Software : PyCharm

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)
        #print(f"循环第{i}次得到的随机数:{random_num}")
        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算法选择将最近最少使用的数据淘汰,保留那些经常被命中的数据。

# -*- coding: utf-8 -*-
# @Time     : 2021/11/28 13:10
# @Author   : wljess
# @File     : 内置缓存模块.py
# @Describe : 
# @Software : PyCharm

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-


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 权限校验

# -*- coding: utf-8 -*-
# @Time     : 2021/11/26 16:08
# @Author   : wljess
# @File     : 权限验证.py
# @Describe : 
# @Software : PyCharm


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 框架路由传参

# -*- coding: utf-8 -*-
# @Time     : 2021/11/26 16:09
# @Author   : wljess
# @File     : 框架路由传参.py
# @Describe : 
# @Software : PyCharm

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())

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值