一、前置说明
1、本节目标
- 了解使用
TypeVar
创建泛型类型。 - 了解类型注解的使用。
- 了解返回
Self
类型注解,支持编辑器如pycharm
智能识别可链式调用的方法。
2、相关回顾
二、操作步骤
1、项目目录
atme
:@me
用于存放临时的代码片断或其它内容。pyparamvalidate
: 新建一个与项目名称同名的package,为了方便发布至pypi
。core
: 用于存放核心代码。tests
: 用于存放测试代码。utils
: 用于存放一些工具类或方法。
2、代码实现
atme/demo/validator_v4/validator.py
import functools import inspect from typing import TypeVar def _error_prompt(value, exception_msg=None, rule_des=None, field=None): default = f'"{value}" is invalid.' prompt = exception_msg or rule_des prompt = f'{default} due to: {prompt}' if prompt else default prompt = f'{field} error: {prompt}' if field else prompt return prompt def raise_exception(func): @functools.wraps(func) def wrapper(self, *args, **kwargs): bound_args = inspect.signature(func).bind(self, *args, **kwargs).arguments exception_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 self return wrapper class 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 = value self._field = field self._rule_des = rule_des def is_string(self, exception_msg=None) -> Self: """ 将返回类型注解定义为 Self, 支持编辑器如 pycharm 智能提示链式调用方法,如:Validator(input).is_string().is_not_empty() - 在 Python 3.5 中引入类型注解的概念,用于支持类型提示和静态类型检查; - 类型注解允许开发者在函数参数、返回值和变量上添加类型信息,但是在运行时,Python 解释器不会检查这些注解是否正确; - 它们主要用于提供给静态类型检查器或代码编辑器进行检查,以提供更好的代码提示和错误检测; - Python 运行时并不强制执行这些注解,Python 依然是一门动态类型的语言。 - 本方法中: - 返回值类型为 bool 类型,用于与装饰器函数 raise_exception 配合使用,校验 self.value 是否通过; - 为了支持编辑器如 pycharm 智能识别链式调用方法,如:Validator(input).is_string().is_not_empty(),将返回类型注解定义为 Self; - Self, 即 'Validator', 由 Self = TypeVar('Self', bound='Validator') 定义; """ return isinstance(self.value, str) def is_not_empty(self, exception_msg=None) -> Self: return bool(self.value)
3、测试代码
atme/demo/validator_v4/test_validator.py
import pytest from atme.demo.validator_v4.validator import Validator def 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 items test_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、要点小结
- 在 Python 3.5 中引入了 类型注解 的概念,用于支持类型提示和静态类型检查;
- 类型注解允许开发者在函数参数、返回值和变量上添加类型信息,但是在运行时,Python 解释器不会检查这些注解是否正确;
- 它们主要用于提供给静态类型检查器或代码编辑器进行检查,以提供更好的代码提示和错误检测;
- Python 运行时并不强制执行这些注解,Python 依然是一门动态类型的语言。
- 定义
Self = TypeVar('Self', bound='Validator')
类型变量,并在校验方法中定义返回注释类型Self
,使编辑器如Pycharm
能够智能提示链式调用方法。
2、下节准备
- 基于
Validator
类实现ParamValidator
,用于校验函数参数