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

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

一、前置说明


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,用于校验函数参数

点击进入《Python装饰器从入门到进阶》总目录

目录
相关文章
|
6月前
|
Python
优化 ParamValidator,让编辑器Pycharm智能提示校验方法
优化 ParamValidator,让编辑器Pycharm智能提示校验方法
79 0
先文章写在自己的富文本上,痛点,商业模式,支持文章转载,搭建工具------自媒体平台之间快速上传文章的方法,利用自己的富文本编辑器进行上传
先文章写在自己的富文本上,痛点,商业模式,支持文章转载,搭建工具------自媒体平台之间快速上传文章的方法,利用自己的富文本编辑器进行上传
|
4月前
|
前端开发 JavaScript Java
文本----简单编写文章的方法(中),后端接口的编写,自己编写好页面就上传到自己的服务器上,使用富文本编辑器进行编辑,想写好一个项目,先分析一下需求,再理一下实现思路,再搞几层,配好参数校验,lomb
文本----简单编写文章的方法(中),后端接口的编写,自己编写好页面就上传到自己的服务器上,使用富文本编辑器进行编辑,想写好一个项目,先分析一下需求,再理一下实现思路,再搞几层,配好参数校验,lomb
|
4月前
|
JavaScript
文本----简单编写文章的方法(上),自己编写好页面就上传到自己的服务器上,使用富文本编辑器进行编辑
文本----简单编写文章的方法(上),自己编写好页面就上传到自己的服务器上,使用富文本编辑器进行编辑
|
Python
【PyCharm】修改编辑器背景
【PyCharm】修改编辑器背景
188 0
ArcGIS配置OSM数据工具ArcGIS Editor for OSM的方法
本文介绍ArcGIS Editor for OpenStreetMap这一工具集插件的下载与安装方法~
454 1
ArcGIS配置OSM数据工具ArcGIS Editor for OSM的方法
|
JavaScript 数据库
codemirror+js-yaml实现YAML格式的文本编辑器以及校验YAML格式是否符合规范的实现
codemirror+js-yaml实现YAML格式的文本编辑器以及校验YAML格式是否符合规范的实现
3004 0
|
Python
pycharm 2020 版取消鼠标悬停显示说明文档的方法
pycharm 2020 版取消鼠标悬停显示说明文档的方法
137 0
|
PyTorch 算法框架/工具 Python
pycharm永久换源方法
pycharm永久换源方法,一次换源以后直接安装第三方库
2265 0
pycharm永久换源方法
|
Python Windows
pycharm编辑器免费版下载安装
pycharm编辑器免费版下载安装,pycharm编辑器及提供专业收费版本,也提供免费版本
201 0
pycharm编辑器免费版下载安装