优化 ParamValidator,让编辑器Pycharm智能提示校验方法

目录

  • 一、前置说明
    • 1、总体目录
    • 2、相关回顾
    • 3、本节目标
  • 二、操作步骤
    • 1、项目目录
    • 2、代码实现
    • 3、测试代码
    • 4、日志输出
  • 三、后置说明
    • 1、要点小结
    • 2、下节准备

一、前置说明

1、总体目录

  • 《 pyparamvalidate 参数校验器,从编码到发布全过程》

2、相关回顾

  • 基于 Validator 类实现 ParamValidator,用于校验函数参数

3、本节目标

  • 了解 __getattribute__ 的特性
  • 使用 __getattribute__ 结合 Validator 类中的方法,让编辑器 Pycharm 智能提示 ParamValidator 类中的方法

二、操作步骤

1、项目目录

  • atme : @me 用于存放临时的代码片断或其它内容。
  • pyparamvalidate : 新建一个与项目名称同名的package,为了方便发布至 pypi
  • core : 用于存放核心代码。
  • tests : 用于存放测试代码。
  • utils : 用于存放一些工具类或方法。

2、代码实现

atme/demo/validator_v6/param_validator.py


import inspect
from functools import wraps
from typing import TypeVar, Callable

from atme.demo.validator_v6.validator import Validator

Self = TypeVar('Self', bound='ParameterValidator')


class ParameterValidator:
    def __init__(self, param_name: str, param_rule_des=None):
        """
        :param param_name: 参数名
        :param param_rule_des: 该参数的规则描述
        """
        self.param_name = param_name
        self.param_rule_des = param_rule_des

        self._validators = []

    def __getattribute__(self, name: str):
        """
        __getattribute__ 在每次访问对象的属性时都会触发,不管属性是否存在。
        通过重写 __getattribute__,可以自定义属性的获取逻辑,实现了对特定属性的直接访问(param_name 、param_rule_des 、 _validators),
        而对于其他属性,则创建名为 validator_method 的函数,将其作为属性返回
        """

        '''
        如果获取到的属性名为 param_name 、param_rule_des 、 _validators , 则使用 object.__getattribute__(self, name) 直接获取对象的属性值。
        不对 self.param_name 、 self.param_rule_des 、 self._validators 做改变
        '''
        if name in ['param_name', 'param_rule_des', '_validators']:
            return object.__getattribute__(self, name)

        '''
        如果属性名不在上述列表中,说明用户正在访问一个不存在的属性,这时创建了一个函数 collect_validator_method
        
        以用户使用 ParamValidator("param").is_string(exception_msg='param must be string').is_not_empty() 为例,代码执行过程如下:

        1. 当用户调用 ParamValidator("param").is_string(exception_msg='param must be string') 时,
        2. 由于 is_string 方法不存在,__getattr__ 方法被调用,返回 validator_method 函数(此时未被调用),is_string 方法实际上是 validator_method 函数的引用,
        3. 当执行 is_string(exception_msg='param must be string') 时,is_string 方法被调用, 使用关键字参数传递 exception_msg='param must be string',
        4. 实际上是执行了 validator_method(exception_msg='param must be string') , validator_method 函数完成调用后,执行函数体中的逻辑:
             - 向 self._validators 中添加了一个元组 ('is_string', (),  {'exception_msg': 'param  must  be  string'})
             - 返回 self 对象
        5. self 对象继续调用 is_not_empty(), 形成链式调用效果,此时的 validator_method 函数的引用就是 is_not_empty, 调用过程与 1-4 相同。
        '''

        def validator_method(*args, **kwargs):
            self._validators.append((name, args, kwargs))
            return self

        return validator_method

    def __call__(self, func: Callable) -> Callable:
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 获取函数的参数和参数值
            bound_args = inspect.signature(func).bind(*args, **kwargs).arguments

            if self.param_name in kwargs:
                # 如果函数被装饰,且以关键字参数传值,则从 kwargs 中取参数值
                value = kwargs[self.param_name]
            else:
                # 如果函数被装饰,且以位置参数传值,则从 bound_args 中取参数值
                value = bound_args.get(self.param_name)

            # 实例化 Validator 对象
            validator = Validator(value, field=self.param_name, rule_des=self.param_rule_des)

            # 遍历所有校验器(注意:这里使用 vargs, vkwargs,避免覆盖原函数的 args, kwargs)
            for method_name, vargs, vkwargs in self._validators:
                # 通过 函数名 反射获取校验函数对象
                validate_method = getattr(validator, method_name)

                # 执行校验函数
                validate_method(*vargs, **vkwargs)

            # 执行原函数
            return func(*args, **kwargs)

        return wrapper

    '''
    ==============================分隔符===============================
    
    以下所有方法,是从 Validator 类中复制过来,目的是:
    
    - 让编辑器如 Pycharm 智能提示 ParameterValidator 本类中可以使用的校验方法;
    - 这些方法仅供 Pycharm 智能提示使用,没有任何实际作用;
        可以是:
            def is_string(self, exception_msg=None) -> Self:
                ...
        也可以是:
            def is_string(self, exception_msg=None) -> Self:
                return isinstance(self.value, str)            
    - ParameterValidator 类的实例通过 __getattribute__ 方法动态收集用户的调用方法;
    - 然后使用 __call__ 方法反射调用 Validator 类中的调用方法
    
    在模块中定义了: Self = TypeVar('Self', bound='ParameterValidator'),目的是:
    
    - 方便从 Validator 类中复制校验方法,粘贴之后不做任何代码层面的修改:
    - 方便链式调用,如: @ParameterValidator("param").is_string().is_not_empty()
    '''

    def is_string(self, exception_msg=None) -> Self:
        return isinstance(self.value, str)

    def is_not_empty(self, exception_msg=None) -> Self:
        return bool(self.value)

3、测试代码

atme/demo/validator_v6/test_param_validator.py


import pytest

from atme.demo.validator_v6.param_validator import ParameterValidator


def test_is_string_validator_passing_01():
    """
    校验一个参数
    """

    @ParameterValidator("param").is_string(exception_msg='param must be string')
    def example_function(param):
        print(param)
        return param

    assert example_function(param="test") == "test"

    with pytest.raises(ValueError) as exc_info:
        example_function(param=123)

    print(exc_info.value)
    assert "invalid" in str(exc_info.value)


def test_is_string_validator_passing_02():
    """
    校验多个参数
    """

    @ParameterValidator("param2").is_string().is_not_empty()
    @ParameterValidator("param1").is_string().is_not_empty()
    def example_function(param1, param2):
        print(param1, param2)
        return param1, param2

    assert example_function("test1", "test2") == ("test1", "test2")

    with pytest.raises(ValueError) as exc_info:
        example_function(123, 123)

    print(exc_info.value)
    assert "invalid" in str(exc_info.value)

4、日志输出

执行 test 的日志如下,验证通过:

============================= test session starts =============================
collecting ... collected 2 items

test_param_validator.py::test_is_string_validator_passing_01 PASSED      [ 50%]test
param error: "123" is invalid. due to: param must be string

test_param_validator.py::test_is_string_validator_passing_02 PASSED      [100%]test1 test2
param2 error: "123" is invalid.


============================== 2 passed in 0.01s ==============================

三、后置说明

1、要点小结

  • __getattribute__ 在每次访问对象的属性时都会触发,不管属性是否存在。
  • 通过重写 __getattribute__,可以自定义属性的获取逻辑,实现了对特定属性的直接访问(param_nameparam_rule_des_validators),而对于其他属性,则创建名为 validator_method 的函数,将其作为属性返回。
  • Validator 类中复制过来的校验方法,是为了让编辑器如 Pycharm 智能提示 ParameterValidator 本类中可以使用的校验方法,没有任何实际作用。
  • 在模块中定义 Self = TypeVar('Self', bound='ParameterValidator'),方便链式调用,如 @ParameterValidator("param").is_string().is_not_empty()
  • 经过优化后,Pycharm 可以正常智能提示可调用的校验方法:

2、下节准备

  • validator 常用校验器的实现

点击返回主目录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/307847.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鱼哥赠书活动第⑥期:《内网渗透实战攻略》看完这本书教你玩转内网渗透测试成为实战高手!!!!

鱼哥赠书活动第⑥期:《内网渗透实战攻略》 如何阅读本书:本书章节介绍:本书大致目录:适合阅读对象:赠书抽奖规则:往期赠书福利: 当今,网络系统面临着越来越严峻的安全挑战。在众多的安全挑战中&…

产品使用说明书也能进行SEO?要怎么制作才能使其易于搜索?

产品使用说明书也能进行SEO?是的,你没有听错,不过是在线化的产品使用说明书。产品使用说明书能通过特定的策略和技巧进行搜索引擎优化(SEO)。这不只是为了让产品信息更易被找到,更是为了提升品牌知名度和用…

ubuntu20.04安装cuda11.4以及cudnn

系统:ubuntu20.04硬件配置:GPU3080、CPU未知通过《软件和更新》在附加驱动选项中添加了驱动: 1.检查自己电脑支持的cuda nvidia-smi4. 下载cuda11.4.2 wget https://developer.download.nvidia.com/compute/cuda/11.4.2/local_installers/c…

昇腾910b部署qwen-7b-chat进行流式输出【pytorch框架】NPU推理

文章目录 准备阶段避坑阶段解决方案一、modeling_qwen.py二、cli_demo.py 结果展示 准备阶段 参考我上一篇文章910b上跑Chatglm3-6b进行流式输出【pytorch框架】 避坑阶段 我在mindspore框架下运行了qwen-7b-base、qwen-7b-chat输出都有大大的问题,参考官方文档 …

C++qt-信号-信号槽

1、概念 信号和槽是两种函数,这是Qt在C基础上新增的特性,类似于其他技术中的回调的概念。 信号和槽通过程序员提前设定的“约定”,可以实现对象之间的通信,有两个先决的条件: 通信的对象必须都是从QObject类中派生出来…

iOS 应用上架指南:资料填写及提交审核

摘要 本文提供了iOS新站上架资料填写及提交审核的详细指南,包括创建应用、资料填写-综合、资料填写-IOS App和提交审核等步骤。通过本指南,您将了解到如何填写正确的资料,并顺利通过苹果公司的审核。 引言 在开发iOS应用后,将其…

视频监控系统EasyCVR如何通过调用API接口查询和下载设备录像?

智慧安防平台EasyCVR是基于各种IP流媒体协议传输的视频汇聚和融合管理平台。视频流媒体服务器EasyCVR采用了开放式的网络结构,支持高清视频的接入和传输、分发,平台提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联…

oracle基本用户管理和权限分配

1.用户与模式的关系,一一对应的关系 2.创建与管理用户 2.1创建用户语法 CREATE user wdf IDENTIFIED by tiger--创建用户wdf,密码tiger DEFAULT tablespace users--用户的默认表空间 quota 10M on users;--在表空间的占用最大空间 注意:用户创建以后…

基于TableAgent实现IT职位招聘数据分析—以传统机器学习与TableAgent 数据分析方式相对比以凸显TableAgent 特性

目录 🚀一. TableAgent—新AI时代的数据分析智能体 🔎1.1 基于DataCanvas Alaya九章元识大模型 🔎1.2 TableAgent的亮点 🚀二. 使用TableAgent分析数据与传统机器学习分析数据对比 🔎2.1 项目背景 🔎2.2 数…

统信UOS命令行设置未签名软件安装权限

原文链接:统信UOS命令行设置未签名软件安装权限 hello,大家好啊!今天我要给大家介绍的是在统信UOS操作系统上通过命令行设置安全中心应用安装权限的方法。在某些情况下,用户可能需要安装未经官方签名的软件包。虽然这可以提供更多…

在python里面探索web框架

一、常识性知识 python Web框架三巨头:Flask(简单易学)、Django(复杂庞大)、FastAPI 1. Django:Django是一个高级的Web框架,它提供了强大的功能和工具,用于快速开发复杂的Web应用程序。 2. Flask&#xff…

2024 CSRankings全球计算机科学排名发布!清华AI第一,哈工大NLP第一

大家好我是二狗。 这两天全球计算机科学排名 CSRankings 2024发布啦! 下面二狗就带大家来看一下最新的排名情况。 清华、北大、上海交大AI领域霸榜前三 在AI板块,主要有人工智能、计算机视觉、机器学习、自然语言处理、网络&信息检索5个细分领域。…

【obj To 3DTiles 格式转换】 可以自定义经纬高、属性表等参数 (一)

目录 0 引言1 3DTiles数据2 objTo3DTiles2.1 工具的安装2.1.1 拓展:Node.js 和 npm 2.2 工具的使用2.2.1 输出成瓦片数据2.2.2 输出带有坐标参数的瓦片数据 3 查看3DTiles数据 🙋‍♂️ 作者:海码007📜 专栏:Cesiumfor…

玄子Share-云计算入门指南

玄子Share-云计算入门指南 一、Windows 键盘字符输入 1. 单键位 直接通过键盘输入即可 2. 双键位 功能键,键盘上显示为两排符号,普通输入模式默认输入下排字符,键盘按下Shift(转换)键,上档键&#xff0…

8 单链表---带表头节点

上节课所学的顺序表的缺点 顺序表的最大问题:插入和删除时需要移动大量元素 链式存储的定义 链式存储的逻辑结构 链表中的基本概念: 注意:表头节点并不属于数据元素 单链表图示: 把3个需要的结构体定义出来: typdef …

矩阵中的最长递增路径

题目链接 矩阵中的最长递增路径 题目描述 注意点 不能 在 对角线 方向上移动或移动到 边界外(即不允许环绕) 解答思路 因为最长递增路径一定是连续的,所以想到使用深度优先遍历来做。如果只使用深度优先遍历会导致超时(同一个…

IP风险画像:源头防范网络攻击的全面策略

在当今数字化的时代,网络攻击呈现多样化和复杂化的趋势,为了确保网络的安全,制定全面的IP风险画像并从源头防范网络攻击是至关重要的。ip数据云将探讨如何通过建立IP风险画像来识别和应对潜在的威胁,从而实现更加安全可靠的网络环…

基于Hologres+Flink的曹操出行实时数仓建设作者:林震|曹操出行实时计算负责人

作者:林震|曹操出行实时计算负责人 曹操出行业务背景介绍 曹操出行创立于2015年5月21日,是吉利控股集团布局“新能源汽车共享生态”的战略性投资业务,以“科技重塑绿色共享出行”为使命,将全球领先的互联网、车联网、…

docker镜像的生成过程

镜像的生成过程 Docker镜像的构建过程,大量应用了镜像间的父子关系。即下层镜像是作为上层镜像的父镜像出现的,下层镜像是作为上层镜像的输入出现。上层镜像是在下层镜像的基础之上变化而来。 FROM centos:7 FROM指令是Dockerfile中唯一不可缺少的命令&a…

2023年12月青少年机器人技术等级考试(四级)理论综合试卷

2023年12月青少年机器人技术等级考试(四级)理论综合试卷 题目总数:30 总分数:100 单选题 第 1 题 单选题 Arduino UNO/Nano主控板,当数字引脚输出信号为高电平时,对应的电压是 ?&…