使用 RaiseExceptionMeta 元类隐式装饰 Validator 类中的所有校验方法

目录

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

一、前置说明

1、总体目录

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

2、相关回顾

  • 使用 raise_exception 装饰器,优化校验方法

3、本节目标

  • 了解元类 metaclass 的使用。
  • 了解 __new__ 方法的使用。
  • 了解如何使用元类隐式装饰类中的所有方法。

二、操作步骤

1、项目目录

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

2、代码实现

atme/demo/validator_v3/validator.py

import functools
import inspectdef _error_prompt(value, exception_msg=None, rule_des=None, field=None):"""优先使用校验方法中的错误提示, 如果方法中没有错误提示,则使用"字段规则描述"代替错误提示拼接出:name error: "123" is invalid. due to: name must be string."""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():# 如果是静态方法,则将它替换为一个新的静态方法,新的静态方法调用 raise_exception 函数,将原静态方法作为参数传递给raise_exceptionif isinstance(value, staticmethod):dct[key] = staticmethod(raise_exception(value.__func__))# 如果是类方法,则将它替换为一个新的类方法,新的类方法调用 raise_exception 函数,将原类方法作为参数传递给raise_exceptionif isinstance(value, classmethod):dct[key] = classmethod(raise_exception(value.__func__))# 如果是普通的成员方法,则将它替换为一个新的函数,新函数调用 raise_exception 函数,将原函数作为参数传递给 raise_exception# 排除掉以双下划线 __ 开头的方法, 如 __init__,__new__等if inspect.isfunction(value) and not key.startswith("__"):dct[key] = raise_exception(value)return super().__new__(cls, name, bases, dct)class Validator(metaclass=RaiseExceptionMeta):def __init__(self, value, field=None, rule_des=None):""":param value: 待校验的值:param field: 校验字段- 用于提示具体哪个字段错误- 如 'name error: name must be string'- error 前面的 `name` 即为 field:param rule_des: 校验规则描述"""self.value = valueself._field = fieldself._rule_des = rule_desdef is_string(self, exception_msg=None):return isinstance(self.value, str)def is_not_empty(self, exception_msg=None):return bool(self.value)

3、测试代码

atme/demo/validator_v3/test_validator.py

import pytestfrom atme.demo.validator_v3.validator import Validatordef test_validator_01():"""在 Validator 实例化时,不给 field、rule_des 传值; 在校验方法中,不给 exception_msg 传值"""validator = Validator('Jane')assert validator.is_string().is_not_empty()with pytest.raises(ValueError) as exc_info:validator = Validator(123)validator.is_string().is_not_empty()assert 'invalid' in str(exc_info.value)print(exc_info.value)  # 输出: "123" is invalid.def test_validator_02():"""在 Validator 实例化时,给 field、rule_des 传值"""validator = Validator('Jane', field='name', rule_des='name must be string from rule des.')assert validator.is_string().is_not_empty()with pytest.raises(ValueError) as exc_info:validator = Validator(123, field='name', rule_des='name must be string from rule des.')validator.is_string().is_not_empty()assert 'name must be string from rule des.' in str(exc_info.value)print(exc_info.value)  # 输出: name error: "123" is invalid. due to: name must be string from rule des.def test_validator_03():"""在 Validator 实例化时,给 field、rule_des 传值; 在校验方法中,给 exception_msg 传值"""validator = Validator('Jane', field='name', rule_des='name must be string from rule des.')assert validator.is_string().is_not_empty()with pytest.raises(ValueError) as exc_info:validator = Validator(123, field='name', rule_des='name must be string from rule des.')validator.is_string('name must be string from method exception msg.').is_not_empty()assert 'name must be string from method exception msg.' in str(exc_info.value)print(exc_info.value)  # 输出: "123" is invalid due to "name error: name must be string from method exception msg."def test_validator_04():"""field_name 为空"""validator = Validator('Jane', rule_des='name must be string from rule des.')assert validator.is_string().is_not_empty()with pytest.raises(ValueError) as exc_info:validator = Validator(123, rule_des='name must be string from rule des.')validator.is_string('name must be string from method exception msg.').is_not_empty()assert 'name must be string from method exception msg.' in str(exc_info.value)print(exc_info.value)  # 输出: "123" is invalid due to "name must be string from method exception msg."

4、日志输出

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

============================= test session starts =============================
collecting ... collected 4 itemstest_validator.py::test_validator_01 PASSED                              [ 25%]"123" is invalid.test_validator.py::test_validator_02 PASSED                              [ 50%]name error: "123" is invalid. due to: name must be string from rule des.test_validator.py::test_validator_03 PASSED                              [ 75%]name error: "123" is invalid. due to: name must be string from method exception msg.test_validator.py::test_validator_04 PASSED                              [100%]"123" is invalid. due to: name must be string from method exception msg.============================== 4 passed in 0.01s ==============================

三、后置说明

1、要点小结

  • 元类 metaclass 可以控制类的创建过程,可以动态的修改类的属性、方法和其他行为,demo 示例如下:

class MyMeta(type):def __new__(cls, name, bases, dct):# 在创建类之前的操作print(f"Creating class: {name}")return super().__new__(cls, name, bases, dct)class MyClass(metaclass=MyMeta):pass# 输出:Creating class: MyClass
  • __new__ 方法用于修改类的行为,在 RaiseExceptionMeta 类中,它会遍历类的字典(包括属性和方法),并对其中的静态方法、类方法和普通成员方法进行修改。
  • __new__ 是在对象实例创建之前调用的方法,用于创建并返回一个新的实例;__init__ 是实例创建之后调用的方法,用于对实例进行初始化。
  • 经过优化后,由于校验方法没有 return self , 导致编辑器如 pycharm 不能智能识别可用的链式调用方法(如下图),对用户很不友好,可以继续优化。

2、下节准备

  • 使用 TypeVar 创建 Self 类变量,方便用户在 pycharm 编辑器中进行链式调用

点击返回主目录

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

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

相关文章

How to talk about your work?

Orial Tutorial English Speaking Practice: Talking About Your Job 🗣️ Hi! Tim here with another 925English lesson! In today’s lesson, we are going to learn how to talk about the work you do in your job. Discussing Your Job Role Overview: L…

中国社科院大学与新加坡新跃社科大学工商管理博士2024申请中

中国社科院大学与新加坡新跃社科大学工商管理博士2024申请中 【项目名称】中国社会科学院大学与新加坡新跃社科大学工商管理博士项目 【学制】最短3年,最长不超过7年 【学位证书】新加坡新跃社科大学工商管理博士学位 【招生对象】企业高管、咨询顾问及其他有经…

Qt QLineEdit文本框控件

文章目录 1 属性和方法1.1 占位字符串1.2 对齐方式1.3 回显模式1.4 读写控制1.5 格式控制1.6 信号和槽 2 实例2. 布局2.2 代码实现 QLineEdit 是Qt 中的文本框,准确地说是单行文本框,通常用于接受用户的输入。 比如用户输入用户名、密码等,都…

ChatGPT付费创作系统V2.5.5独立版+前端

ChatGPT付费创作系统V2.5.5版本优化了很多细节,功能增加增加长篇写作功能。该版本为编译版无开源,本版本特别处理了后台弹窗、暗链网址。特别优化了数据库。升级过程中未发现任何BUG,全新安装或者升级安装均未出现400或者500错误,…

【数据结构专题】「延时队列算法」史上手把手带你认识一下数据结构的基本概念与术语

在本节中,我们将对一些概念和术语赋以确定的含义,以便与读者取得“共同的语言”。这些概念和术语将在以后的章节中多次出现。 数据 概念 数据(data) 是对客观事物的符号表示, 在计算机科学中是指所有能输人到计算机中并被计算机程序处理的…

Python 工具 | conda 基本命令

Hi,大家好,我是源于花海。本文主要了解 Python 的工具的 conda 相关的基本命令。Conda 是一个开源的软件包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖关系,并在它们之间轻松切换。在Windows下,需要安装…

Mobile Aloha 【软硬件原理+代码解析】

1. Mobile ALOHA Hardware2. Imitation Learning3. Co-training with Static ALOHA Data4. Task Setting5. Experiments5.1 ACT5.2 对比ACT、Diffusion Policy和VINN 6. Software Code Analyze Mobile ALOHA: 利用低成本全身远程操作系统学习复杂的双手移动操作技能 [译] 硬件代…

#{}和${}的区别?

#{}是占位符,预编译处理;${}是拼接符,字符串替换,没有预编译处理。Mybatis在处理#{}时,#{}传入参数是以字符串传入,会将SQL中的#{}替换为?号,调用PreparedStatement的set方法来赋值。Mybatis在…

NonCopyable

NonCopyable 是 C 用于阻止派生类生成拷贝构造和拷贝赋值函数的一个通用类,在很多地方都有使用,例如 arm、boost等,在 effective c 中也有讲到,以下是一种使用方法: class NonCopyable { private:NonCopyable(const N…

Dart中令人惊艳的用法

Dart是谷歌开发的现代化编程语言,凭借其简洁的语法和强大的功能,在开发者当中赢得了极高的声誉,尤其是在Flutter框架中发挥了巨大的作用。本文将介绍Dart中的8个令人惊艳的用法,这些用法不仅技术深度足够,充满启发性&a…

Elasticsearch基本操作之文档操作

本文来说下Elasticsearch基本操作之文档操作 文章目录 文档概述创建文档示例创建文档(生成随机id)创建文档(自定义唯一性标识) 查看文档示例根据主键查看文档查看所有文档 修改文档示例全局修改文档局部修改文档 删除文档示例根据文档的唯一性标识删除文档条件删除文档 本文小结…

C++之返回值优化

返回值优化(Return value optimization,缩写为RVO)是C的一项编译优化技术。它最大的好处是在于: 可以省略函数返回过程中复制构造函数的多余调用,解决 “C 中长久以来为人们所诟病的临时对象的效率问题”。 我们先谈正…

【leetcode 447. 回旋镖的数量】审慎思考与推倒重来

447. 回旋镖的数量 题目描述 给定平面上 **n **对 互不相同 的点 points ,其中 points[i] [xi, yi] 。回旋镖 是由点 (i, j, k) 表示的元组 ,其中 i 和 j 之间的距离和 i 和 k 之间的欧式距离相等(需要考虑元组的顺序)。 返回平…

Linux引导过程和服务

一、Linux操作系统引导过程 1.引导过程 bios 加电自检——mbr——grub——加载内核——启动进程 加电后BIOS程序回自检硬件,硬件无故障后,会根据第一次启动项去找内核,一般来说第一启动项是硬盘,找到硬盘后,会根据mb…

2. Mybatis 中SQL 执行原理

这里有两种方式,一种为常用的 Spring 依赖注入 Mapper 的方式。另一种为直接使用 SqlSessionTemplate 执行 Sql 的方式。 Spring 依赖注入 Mapper 的方式 Mapper 接口注入 SpringIOC 容器 Spring 容器在扫描 BeanDefinition 阶段会扫描 Mapper 接口类&#xff0c…

深入了解网络流量清洗--使用免费的雷池社区版进行防护

​ 随着网络攻击日益复杂,企业面临的网络安全挑战也在不断增加。在这个背景下,网络流量清洗成为了确保企业网络安全的关键技术。本文将探讨雷池社区版如何通过网络流量清洗技术,帮助企业有效应对网络威胁。 ![] 网络流量清洗的重要性&#x…

第四站:指针的进阶-(二级指针,函数指针)

目录 二级指针 二级指针的用途 多级指针的定义和使用 指针和数组之间的关系 存储指针的数组(指针数组:保存地址值) 指向数组的指针(数组指针) 传参的形式(指针) 数组传参时会退化为指针 void类型的指针 函数指针 定义: 调用:两种方式:(*指针名)(参数地址) 或者 指针…

别闹了,真的不是你的技术菜!!!

最近经常听到有小伙伴总是在抱怨自己的技术菜,公司没有机会让自己去成长技术,于是小编就此场景来写一篇文章,希望对大家有帮助。 错误的理解CRUD工程师 CRUD工程师这个名称是很多小伙伴都听过的,并且很多工程师都把自己比作是代…

Ubuntu 18.04.5 LTS 解决安装包复杂依赖相关问题解决的主要法则和VIM的安装实录

前言:目标和环境 环境: Ubuntu 18.04.5 LTSVMware 目标: 安装vim,解决包依赖的冲突: 本文,通过一个很好的实例,诠释了,LINUX系统下,安装一个应用遇到的依赖库问题如何…

Wilcoxon秩和检验-校正P值(自备)

R语言 boxplot作图 图内展示校正后的P值(padj)_r语言 p值校正-CSDN博客 FDR错误发现率-P值校正学习_fdr和p值的关系-CSDN博客 原理介绍: Benjamini-Hochberg 方法介绍 有N次假设检验,对每一次假设检验都计算其P值,然后将计算出的P值按照…