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

目录

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

一、前置说明

1、总体目录

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

2、相关回顾

  • 使用 TypeVar 创建 Self 类型变量,方便用户在 Pycharm 编辑器中链式调用校验方法

3、本节目标

  • 了解 __getattr__ 的特性。
  • 了解 __call__ 的用法。
  • 了解如何在一个类中动态的使用另一个类中的方法。

二、操作步骤

1、项目目录

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

2、代码实现

atme/demo/validator_v5/validator.py


import functools
import inspect
from typing import TypeVardef _error_prompt(value, exception_msg=None, rule_des=None, field=None):default = f'"{value}" is invalid.'prompt = exception_msg or rule_desprompt = f'{default} due to: {prompt}' if prompt else defaultprompt = f'{field} error: {prompt}' if field else promptreturn promptdef raise_exception(func):@functools.wraps(func)def wrapper(self, *args, **kwargs):bound_args = inspect.signature(func).bind(self, *args, **kwargs).argumentsexception_msg = kwargs.get('exception_msg', None) or bound_args.get('exception_msg', None)error_prompt = _error_prompt(self.value, exception_msg, self._rule_des, self._field)result = func(self, *args, **kwargs)if not result:raise ValueError(error_prompt)return selfreturn wrapperclass RaiseExceptionMeta(type):def __new__(cls, name, bases, dct):for key, value in dct.items():if isinstance(value, staticmethod):dct[key] = staticmethod(raise_exception(value.__func__))if isinstance(value, classmethod):dct[key] = classmethod(raise_exception(value.__func__))if inspect.isfunction(value) and not key.startswith("__"):dct[key] = raise_exception(value)return super().__new__(cls, name, bases, dct)'''
- TypeVar 是 Python 中用于声明类型变量的工具
- 声明一个类型变量,命名为 'Self', 意思为表示类的实例类型
- bound 参数指定泛型类型变量的上界,即限制 'Self' 必须是 'Validator' 类型或其子类型
'''
Self = TypeVar('Self', bound='Validator')class Validator(metaclass=RaiseExceptionMeta):def __init__(self, value, field=None, rule_des=None):self.value = valueself._field = fieldself._rule_des = rule_desdef is_string(self, exception_msg=None) -> Self:"""将返回类型注解定义为 Self, 支持编辑器如 pycharm 智能提示链式调用方法,如:Validator(input).is_string().is_not_empty()- 从 Python 3.5 版本开始支持类型注解- 在 Python 3.5 中引入了 PEP 484(Python Enhancement Proposal 484),其中包括了类型注解的概念,并引入了 typing 模块,用于支持类型提示和静态类型检查;- 类型注解允许开发者在函数参数、返回值和变量上添加类型信息,但是在运行时,Python 解释器不会检查这些注解是否正确;- 它们主要用于提供给静态类型检查器或代码编辑器进行,以提供更好的代码提示和错误检测;- Python 运行时并不强制执行这些注解,Python 依然是一门动态类型的语言。- 本方法中:- 返回值类型为 bool 类型,用于与装饰器函数 raise_exception 配合使用,校验 self.value 是否通过;- 为了支持编辑器如 pycharm 智能识别链式调用方法,将返回类型注解定义为 Self, 如:Validator(input).is_string().is_not_empty();- Self, 即 'Validator', 由 Self = TypeVar('Self', bound='Validator') 定义;- 如果返回类型不为 Self, 编辑器如 pycharm 在 Validator(input).is_string() 之后,不会智能提示 is_not_empty()"""return isinstance(self.value, str)def is_not_empty(self, exception_msg=None) -> Self:return bool(self.value)

atme/demo/validator_v5/param_validator.py


import inspect
from functools import wraps
from typing import Callablefrom atme.demo.validator_v5.validator import Validatorclass ParameterValidator:def __init__(self, param_name: str, param_rule_des=None):""":param param_name: 参数名:param param_rule_des: 该参数的规则描述"""self.param_name = param_nameself.param_rule_des = param_rule_desself._validators = []def __getattr__(self, name: str):"""当调用一个不存在的属性或方法时,Python 会自动调用 __getattr__ 方法,因此可以利用这个特性,动态收集用户调用的校验方法。以用户使用 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 selfreturn validator_methoddef __call__(self, func: Callable) -> Callable:"""使用 __call__ 方法, 让 ParameterValidator 的实例变成可调用对象,使其可以像函数一样被调用。'''@ParameterValidator("param").is_string()def example_function(param):return paramexample_function(param="test")'''以这段代码为例,代码执行过程如下:1. 使用 @ParameterValidator("param").is_string() 装饰函数 example_function,相当于: @ParameterValidator("param").is_string()(example_function)2. 此时返回一个 wrapper 函数(此时未调用), example_function 函数实际上是 wrapper 函数的引用;3. 当执行 example_function(param="test") 时,相当于执行 wrapper(param="test"), wrapper 函数被调用,开始执行 wrapper 内部逻辑, 见代码中注释。"""@wraps(func)def wrapper(*args, **kwargs):# 获取函数的参数和参数值bound_args = inspect.signature(func).bind(*args, **kwargs).argumentsif self.param_name in kwargs:# 如果用户以关键字参数传值,如 example_function(param="test") ,则从 kwargs 中取参数值;value = kwargs[self.param_name]else:# 如果用户以位置参数传值,如 example_function("test"),则从 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

3、测试代码

atme/demo/validator_v5/test_param_validator.py


import pytestfrom atme.demo.validator_v5.param_validator import ParameterValidatordef test_is_string_validator_passing_01():"""校验一个参数"""@ParameterValidator("param").is_string(exception_msg='param must be string')def example_function(param):print(param)return paramassert 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, param2assert 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 itemstest_param_validator.py::test_is_string_validator_passing_01 PASSED      [ 50%]test
param error: "123" is invalid. due to: param must be stringtest_param_validator.py::test_is_string_validator_passing_02 PASSED      [100%]test1 test2
param2 error: "123" is invalid.============================== 2 passed in 0.01s ==============================

三、后置说明

1、要点小结

  • 当调用一个不存在的属性或方法时,Python 会自动调用 __getattr__ 方法,可以利用这个特性,动态收集用户调用的校验方法。
  • 使用 __call__ 方法, 让 ParameterValidator 的实例变成可调用对象,使其可以像函数一样被调用。
  • 可以结合使用 __getattr____call__ 方法,实现在一个类中动态调用另一个类中的方法。
  • 虽然从功能上实现了校验函数参数的功能,但由于 ParameterValidator 并没有显式的定义 is_string()is_not_empty() 方法,编辑器无法智能提示可校验方法,需要进一步优化。

2、下节准备

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

点击返回主目录

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Every Nobody Is Somebody 「每小人物都能成大事」

周星驰 NFT Nobody即将发售,Nobody共创平台 Every Nobody Is Somebody Nobody 关于Nobody:Nobody是一款Web3共创平台,旨在为创作者提供一个交流和合作的场所,促进创意的产生和共享。通过该平台,创作者可以展示自己的作…

git秘钥过期 ERROR: Your SSH key has expired

文章目录 1、错误提示Your SSH key has expired2、登录Github确认3、重新设置秘钥 1、错误提示Your SSH key has expired 使用git命令时遇到Github 的 SSH Key秘钥过期,提示错误ERROR: Your SSH key has expired 2、登录Github确认 首先登录Github查看&#xff…

某查查请求头参数加密分析(含JS加密算法与Python爬虫源码)

文章目录 1. 写在前面2. 请求分析3. 断点分析4. 扣加密JS5. Python爬虫代码实现 【作者主页】:吴秋霖 【作者介绍】:Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作! 【作者推荐】&#xff…

基于SELinux三权分立配置方法

1.系统安装 系统安装完成后,系统当前的SELinux配置为: # cat /etc/selinux/config SELINUX=enforcing SELINUXTYPE=targeted 2.SELinux环境准备 # yum install setools policycoreutils.x86_64 selinux-policy-mls.noarch setroubleshoot.x86_64 setools-console -y 3.SELin…

手撕单链表(单向,不循环,不带头结点)的基本操作

𝙉𝙞𝙘𝙚!!👏🏻‧✧̣̥̇‧✦👏🏻‧✧̣̥̇‧✦ 👏🏻‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - :来于“云”的“羽球人”。…

mercury靶机

文章妙语 不与伪君子争名,不与真小人争利,不与执拗人争理,不与匹夫争勇,不与酸儒争才。不与蠢人施恩 一、信息收集 主机探测 端口探测 探测主机详细版本信息 8080开了http服务 目录扫描 robots.txt目录下什么也没有 二&#xff0…

Python | Iter/genartor | 一文了解迭代器、生成器的含义\区别\优缺点

前提 一种技术的出现,需要考虑: 为了实现什么样的需求;遇到了什么样的问题;采用了什么样的方案;最终接近或达到了预期的效果。 概念 提前理解几个概念: 迭代 我们经常听到产品迭代、技术迭代、功能迭代…

零基础学习数学建模——(二)数学建模的步骤

本篇博客将详细介绍数学建模的步骤。 文章目录 引例:年夜饭的准备第一步:模型准备第二步:模型假设第三步:模型建立第四步:模型求解第五步:结果分析第六步:模型检验第七步:模型应用及…

openeuler的安装和两台linux主机配置ssh实现互相免密登陆

一、openeuler的安装 下载OpenEuler - 网址:https://www.openeuler.org/zh/download/archive/ - 版本选择:openEuler 22.03 LTS SP2 (镜像文件) ,即长期更新版 设置自定义硬件 内存:推荐2GB 处理器&…

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

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

rke2 Offline Deploy Rancher v2.8.0 latest (helm 离线部署 rancher v2.8.0)

文章目录 预备条件为什么是三个节点?​预备条件配置私有仓库介质清单安装 helm安装 cert-manager下载介质镜像入库helm 部署卸载 安装 rancher镜像入库helm 安装 验证 预备条件 所有支持的操作系统都使用 64-bit x86 架构。Rancher 兼容当前所有的主流 Linux 发行版…

如何通过兴趣爱好选职业?

一个错误的选择,可能造成终身的遗憾,一个正确的选择,可以让我们少奋斗几十年。所以无论现在付出多少代价,多花一些时间,去研究以下未来的职业方向,这是值得的。 职业定位(专业定位)…

深兰科技AI医疗健康产品获3000台采购订单

12月6日,武汉某企业与深兰科技签署协议,一次性采购3000台深兰科技AI生理健康检测仪——扁鹊。 深兰科技AI生理健康检测仪——扁鹊是深兰科技推出的人体生理指标检测产品。基于AI生物技术、融合互联网医疗及AIoT技术,深兰科技AI生理健康检测仪…

YOLOv8-Seg改进:轻量化改进 | 华为Ghostnet,超越谷歌MobileNet | CVPR2020

🚀🚀🚀本文改进: Ghost bottleneck为堆叠Ghost模块 ,与YOLOV8建立轻量C2f_GhostBottleneck 🚀🚀🚀YOLOv8-seg创新专栏:http://t.csdnimg.cn/KLSdv 学姐带你学习YOLOv8,从入门到创新,轻轻松松搞定科研; 1)手把手教你如何训练YOLOv8-seg; 2)模型创新,提…

HubSpot CRM:卓越客户服务的关键引擎

在数字化时代,提供卓越的客户服务是企业成功的关键之一。HubSpot CRM以其强大的功能和灵活性,成为实现卓越客户服务的关键引擎,以下是强调HubSpot CRM在客户服务中的应用的关键方面: 1. 全面的客户视图 HubSpot CRM集成了全面的…

《路由与交换技术》---练习题(无答案纯享版)

注意!!!这篇blog是无答案纯享版的 选择填空的答案我会放评论区 简答题可以看这里 计算题可以发私信问我(当然WeChat也成)but回讯息很慢 一、选择题 1.以下不会在路由表里出现的是: ( ) A.下一跳地址 B.网络地址 C…

科大讯飞星火大模型加持数字员工系列产品发布

面对时代浪潮,基业长青的企业总会率先拥抱变化,在时代交替中创造新的增长空间。当数字化浪潮涌入千行百业,企业掌舵者如何选择转型? 从数字员工到灯塔工厂,愈发成熟的人工智能技术已深入企业管理,持续提高…

小白苦恼:电脑那么多USB口,怎么知道哪个读写更快?

前言 最近有个朋友和小白抱怨:电脑那么多USB接口,有些接口在传输文件的时候实在慢的很。 电脑诞生以来,USB接口就一直存在。但是USB接口还是长得几乎一样,不仔细去研究都不知道哪个USB会更快。 许多小伙伴就会直接放弃辨认&…

计算机导论03-计算机组成

计算机系统结构 冯•诺依曼体系结构 冯•诺依曼体系结构的基本要点 冯•诺依曼思想即冯•诺依曼体系结构思想,其最基本的概念是存储程序概念,它奠定了现代计算机的结构基础。 功能部件: 计算机必须具备五大基本组成部件,包括:运…

程序员试用期转正工作总结

一、试用期工作总结 在公司的三个月试用期中,我完成了以下工作: 完成了XX个功能模块的开发,包括XX模块、XX模块和XX模块。参与了XX个项目的开发和上线,其中XX项目、XX项目和XX项目是我主导的。优化了现有系统的性能,特…