Python3_typing_数据类型提示_pydantic数据验证

Python3 类型提示及数据验证

基于Python typing的数据验证

Python3.5+

Python3.5+ 之后,Python官方内置了一个数据类型标注的方式, 方便提高代码健壮性,捕获逻辑错误,后面代码示例解释。

简单的例子


from typing import List
# 比如创建一个函数  
def greeting(name: str, hobby: List[str]) -> str:
    """
    :param name: str类型参数
    :param hobby: 只包含str的List类型参数
    :return: str类型返回值
    """

    return 'Hello ' + name + ",".join(hobby)


# 检测传入类型为对象实例
class HaHa(object):
    def __init__(self, a=1):
        self.a = a

    def run(self):
        print(self.a)


# 检测传入类型为对象 HaHa 的实例
def check_obj(n1: HaHa) -> None:
    # 使用ide 这里run()方法会有友好的提示
    n1.run()


h = HaHa(666)

# check_obj函数返回None,用一个参数接收,ide会提示语法逻辑问题
n = check_obj(h)

这样写有很多好处,最直观的就是ide的语法提示, 这个在 C, Java 里面是很基本语法,而且是强制要写返回值的;

稍微复杂点


from typing import List, Callable, Any, Union, Optional


# 类型别名 不重要
Vector = List[float]


# def scale(scalar: float, vector: List[float]) -> List[float]:  # 同下一种写法
def scale(scalar: float, vector: Vector) -> Vector:  # Vector 对上面的简写
    """

    :param scalar: float 类型的参数
    :param vector: Vector 类型, 即包含float类型的List
    :return:
    """
    return [scalar * num for num in vector]


print(scale(1.2, [1.2, 2.2]))


def foo(f1: int, f2: int) -> int:
    return f1 * f2


# 回调函数类型 格式 Callable[[参数1类型, 参数2类型,..], 返回值类型] Any 任意类型 默认=1
def bar(b1: int, next_item: Callable[[int, int], int], b2: Any = 1) -> int:
    return b1 + b2 + next_item(2, b1)


print(bar(2, foo))


def my_foo(n: Union[str, int]) -> str:
    """
    Union 允许一个参数多种数据类型
    :param n:
    :return:
    """
    return str(n)


def my_bar(n: Optional[str, int]) -> str:
    """
    Optional 也是允许一个参数多种数据类型
    Optional[x] 相当于 Union[x, None]的简写 官网描述: https://docs.python.org/zh-cn/3/library/typing.html#typing.Optional
    :param n:
    :return:
    """
    return str(n)

# 使用pydantic Union的一些细节
# 比如 Union[str, int, UUID], 最好是把具体的类型写在最前面 
# 改成这样最好 Union[UUID, int, str]
# 详情参考: https://pydantic-docs.helpmanual.io/usage/types/#unions

更多例子可以参考官网 https://docs.python.org/zh-cn/3/library/typing.html#module-typing

注意

当然上述类型 你不遵守也可以传值,调用,如下:

from typing import List, Union


def test_params(n1: List[float]) -> str:

    # 这里 一般ide就会提示逻辑错误 join函数 期望是str类型的元素
    return ",".join(n1)

# 如果n1 参数List包含多种类型应该这样写
# def test_params(n1: List[Union[str, int]]) -> str:

# 传入List[str]类型的参数  ide 就会提示逻辑错误
print(test_params(["abc", "12"]))

# 传入List[float]  会报错
print(test_params([1.3, 2.3]))

上面,示例代码,有几处明显错误,代码注释中有解释原因,借助ide提示会更直接,比如pycharm 有逻辑问题的代码,背景颜色就会有区别。

使用第三方库验证数据

pydantic 是一个类型注释的数据验证


import traceback
from datetime import datetime
from typing import List, Union, Optional
from pydantic import BaseSettings, AnyHttpUrl, IPvAnyAddress, EmailStr, BaseModel, ValidationError


class Config(BaseSettings):
    """ 验证配置文件参数格式 """
    MYSQL_USERNAME: str = ""
    MYSQL_PASSWORD: str = ""
    MYSQL_HOST: Union[AnyHttpUrl, IPvAnyAddress] = ""
    MYSQL_DATABASE: str = ""
    EMAIL_TEST_USER: EmailStr = "" 



class User(BaseModel):
    """ 验证参数类型 """
    id: int
    name = 'John Doe'
    signup_ts: Optional[datetime] = None
    friends: List[int] = []


# 正确的数据
correct_data = {
    'id': '123',
    'signup_ts': '2019-06-01 12:22',
    'friends': [1, 2, '3']
}

# 错误的数据
error_data = {
    "id": "aaa",  # 规定id为int类型 错误
    "name": "Floyd",
    'signup_ts': '2019-06-01 12:22',
    'friends': ["a", 2, '3']  # 规定List里面全是int类型 错误
}


def validation_data(check_data):
    try:
        user = User(**check_data)
    except ValidationError as e:  # 验证失败的时候
        # traceback 打印错误栈
        print(traceback.format_exc())

        # 具体输出那里错误了
        print(e.json())
        return
    print(user.id)
    # > 123
    print(repr(user.signup_ts))
    print(user.signup_ts)
    # > datetime.datetime(2019, 6, 1, 12, 22)
    print(user.friends)
    # > [1, 2, 3]
    print(user.dict())


if __name__ == '__main__':
    validation_data(correct_data)
    validation_data(error_data)

# 第一个函数输出
"""
数据验证正确,正常输出
123
datetime.datetime(2019, 6, 1, 12, 22)
2019-06-01 12:22:00
[1, 2, 3]
{'id': 123, 'signup_ts': datetime.datetime(2019, 6, 1, 12, 22), 'friends': [1, 2, 3], 'name': 'John Doe'}
"""

# 第二个函数输出
"""
# print(traceback.format_exc()) 打印的错误栈
Traceback (most recent call last):
  File "/file_path/demo.py", line 49, in validation_data
    user = User(**check_data)
  File "pydantic/main.py", line 338, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 2 validation errors for User
id
  value is not a valid integer (type=type_error.integer)
friends -> 0
  value is not a valid integer (type=type_error.integer)

# e.json() 友好的输出错误提示
[
  {
    "loc": [
      "id"
    ],
    "msg": "value is not a valid integer",
    "type": "type_error.integer"
  },
  {
    "loc": [
      "friends",
      0
    ],
    "msg": "value is not a valid integer",
    "type": "type_error.integer"
  }
]

"""

上述的e.json()可以很友好的输出参数错误提示。

pydantic常见用途

  • 固定参数配置验证格式,比如配置文件参数格式, 可参考FastAPI项目配置文件;
  • 封装成装饰器,验证传入的参数,方便校验参数,捕获到异常,全局捕获ValidationError异常, 然后更友好的提示,参数异常;

参考


文章作者: 王小右
版权声明: 咳咳想白嫖文章?本文章著作权归作者所有,任何形式的转载都请注明出处。 https://www.charmcode.cn !
  目录