简洁的 Python Schema
Python Schema使用说明
项目地址:GitHub
1. Schema是什么?
不管我们做什么应用,只要和用户输入打交道,就有一个原则--永远不要相信用户的输入数据。意味着我们要对用户输入进行严格的验证,web开发时一般输入数据都以JSON形式发送到后端API,API要对输入数据做验证。一般我都是加很多判断,各种if,导致代码很丑陋,能不能有一种方式比较优雅的验证用户数据呢?Schema就派上用场了。
Schema非常简单,也就几百行的代码,最核心的类就一个:Schema。
2. 安装
pip install schema
1. 给Schema类传入类型(int、str、float等)
例如:
from schema import Schema
Schema(int).validate(10)
# 10
Schema(int).validate('10')
# SchemaUnexpectedTypeError: '10' should be instance of 'int'
可见Schema会去验证validate方法传入的对象是不是所指定的类型,是则返回传入的数据,否则抛出一个SchemaError的异常(SchemaUnexpectedTypeError是SchemaError的子类)。
2. 给Schema类传入可调用的对象(函数、带__call__的类等)
例如:
Schema(lambda x: 0<x<10).validate(5)
# 5
Schema(lambda x: 0<x<10).validate(57)
# SchemaError: <lambda>(57) should evaluate to True
自定义函数:
def secre_validate(password):
password_list = ["123456", "666666", "888888", "abcdef", "aaaaaa", "112233"]
if password in password_list:
return False
return True
Schema(secre_validate, error="字符串已经存在").validate("123456")
# schema.SchemaError: 字符串已经存在
可见Schema会把validate方法传入的值传入到对应的函数里面作为参数,如果函数返回值为True则返回输入数据,否则抛出异常。
3. 给Schema类传入带有validate方法的对象
Schema也内置了一些类(Use、And、Or等等),这些类的实例都带有validate方法,亦可作为Schema的参数传入,例如:
from schema import Schema, And
# And代表两个条件必须同时满足
Schema(And(str, lambda s: len(s) > 2)).validate('abcd')
# 'abcd'
4. 给Schema类传入容器对象(list、tuple、set等)
例如:
Schema([int, float]).validate([1, 2, 3, 4.0])
# [1, 2, 3, 4.0]
相当于,对于[1, 2, 3, 4.0]当中的任何一个元素,必须是int或者float才行(注意是or的关系)
5. 给Schema传入一个字典对象(大部分使用Schema的场景都是传入字典对象,这个很重要)
Schema({'name': str, 'age': int}).validate({'name': 'foobar', 'age': 18})
# {'age': 18, 'name': 'foobar'}
Schema({'name': str, 'age': int}).validate({'name': 'foobar'})
# SchemaMissingKeyError: Missing keys: 'age'
首先,明确两个概念,Schema类传入的字典,称之为模式字典,valdiate方法传入的字典称之为数据字典。
首先,Schema会判断, 模式字典和数据字典的key是否完全一样,不一样的话直接抛出异常。如果一样,就去拿数据字典的value去验证模式字典相应的value,如果数据字典的全部value都可以验证通过的话才返回数据,否则抛出异常,是不是感觉这种验证顿时感觉清爽了呢?
6. faqs
6.1 Schema传入字典很好用,但是我有的数据是可选的,也就是说有的key可以不提供怎么办?
from schema import Optional, Schema
Schema({'name': str, Optional('age'): int}).validate({'name': 'foobar'})
# {'name': 'foobar'}
Schema({'name': str, Optional('age', default=18): int}).validate({'name': 'foobar'})
# {'age': 18, 'name': 'foobar'}
6.2 禁止传入某个key:Forbidden
Forbidden可以将某个key禁止:
from schema import Schema, Forbidden
Schema({Forbidden('name'): str, 'age': int}).validate({"age": 15}) # {"age": 15}
Schema({Forbidden('name'): str, 'age': int}).validate({"name": "laozhang", "age": 15}) # schema.SchemaForbiddenKeyError
Schema({Forbidden('name'): str, 'age': int}).validate({"name": 10, "age": 15}) # schema.SchemaWrongKeyError
值得注意的是,与禁用秘钥配对的值将决定了它是否会被拒绝:
from schema import Schema, Forbidden
Schema({Forbidden('name'): int, 'name': str}).validate({'name': 'laozhang'}) # {'name': 'laozhang'}
Schema({Forbidden('name'): str, 'name': str}).validate({'name': 'laozhang'}) # schema.SchemaForbiddenKeyError
另外,Forbidden的优先级要比Optional要高:
from schema import Schema, Forbidden, Optional
Schema({Forbidden('name'): str, Optional('name'): str}).validate({"name": "laozhang"}) # schema.SchemaForb
6.3 我想让Schema只验证传入字典中的一部分数据,可以有多余的key但是不要抱错,怎么做?
Schema({'name': str, 'age': int}, ignore_extra_keys=True).validate({'name': 'foobar', 'age': 100, 'sex': 'male'})
# {'age': 100, 'name': 'foobar'}
6.4 Schema抛出的异常信息不是很友好,我想自定义错误信息,怎么办?
Schema自带的类(Use、And、Or、Regex、Schema等)都有一个参数error,可以自定义错误信息
Schema({'name': str, 'age': Use(int, error='年龄必须是整数')}).validate({'name': 'foobar', 'age': 'abc'})
# SchemaError: 年龄必须是整数
6.5 Use
Use在验证的时候,会自动帮你转换它的值
from schema import Schema, Use
print Schema(Use(int)).validate(10) # 10-->int类型
print Schema(Use(int)).validate('10') # 10-->int类型
print Schema(Use(int)).validate('xiaoming') # schema.SchemaError
6.6 Const
我们知道Use在验证的时候,会自动帮你转换它的值。Const可以保持原始数据不变:
from schema import Schema, Use, Const
print Schema(Const(Use(int))).validate('10') # 10-->str类型
7. 一个稍微复杂的例子:
from schema import Schema, And, Optional, SchemaError, Regex
def name_check(name):
password_list = ["root", "admin", "888888", "baba", "aaaaaa", "112233"]
if name in password_list:
return False
return True
schema = {
"id": And(int, lambda x: 100 <= x, error="id必须是整数,大于等于100"),
"name": And(str, name_check, error="name已经存在"),
"price": And(float, lambda x: 111 > x > 0, error="price必须是大于0小于111的小数"),
"data": {
"password": And(Regex("[a-z0-9A-Z]{8,20}"), error="密码为数字、字母,长度8-20"),
"email": And(Regex("^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"), error="email格式错误")
},
Optional("info", default="这个key可以不提供,我设置了这一段默认值。"): str,
}
data = {
"id": 111,
"name": "jarvis",
"price": 9.5,
"data": {
"password": "222agfwetAAA",
"email": "www.qq.afsdf@email.cn",
}
}
try:
a = Schema(schema).validate(data)
except SchemaError as e:
print(e)
else:
print("验证成功!\n{}".format(a))
# 验证成功!
# {'id': 111, 'name': 'jarvis', 'price': 9.5, 'data': {'password': '222agfwetAAA', 'email': 'www.qq.afsdf@email.cn'}, 'info': '这个key可以不提供,我设置了这一段默认值。'}
参考文章:https://segmentfault.com/a/1190000011777230
简洁的 Python Schema的更多相关文章
- Python Schema使用说明
转自https://segmentfault.com/a/1190000011777230 Python Schema使用说明 Schema是什么? 不管我们做什么应用,只要和用户输入打交道,就有一个 ...
- 写出简洁的Python代码: 使用Exceptions(转)
add by zhj: 非常好的文章,异常在Python的核心代码中使用的非常广泛,超出一般人的想象,比如迭代器中,当我们用for遍历一个可迭代对象时, Python是如何判断遍历结束的呢?是使用的S ...
- 简洁的python测试框架——Croner
[本文出自天外归云的博客园] Croner简介 这是一个非常简洁的测试框架,是基于python3的nose进行二次开发的. Github地址 可以随意的在此基础上进行扩展以支持jenkins等其他扩展 ...
- Thonny -- 简洁的 python 轻量级 IDE
Thonny目前是 树莓派 上 默认的 Python 开发环境. 该 IDE 是 Institute of Computer Science of University of Tartu (爱沙尼亚 ...
- Python和Java的语法对比,语法简洁上python的确完美胜出
Python是一种广泛使用的解释型.高级编程.通用型编程语言,由吉多·范罗苏姆创造,第一版发布于1991年.可以视之为一种改良(加入一些其他编程语言的优点,如面向对象)的LISP.Python的设计哲 ...
- 一个极其简洁的Python网页抓取程序,自己主动从雅虎財经抓取股票数据
本程序使用Python 2.7.6编写,扩展了Python自带的HTMLParser,自己主动依据预设的股票代码列表,从Yahoo Finance抓取列表中的数据日期.股票名称.实时报价.当日变化率. ...
- 【转】Python Schema一种优雅的数据验证方式
转自 https://segmentfault.com/a/1190000011777230 Schema是什么? 不管我们做什么应用,只要和用户输入打交道,就有一个原则--永远不要相信用户的输入数据 ...
- Python join()函数
今天写python 100例时,有个题目是大致是这样的:已知输入形式是1+3+2+1,要求输出形式为1+1+2+3 一开始思路是将输入的字符串用split()函数划分成数组,在对数组进行排序,再用fo ...
- Python 函数式编程 & Python中的高阶函数map reduce filter 和sorted
1. 函数式编程 1)概念 函数式编程是一种编程模型,他将计算机运算看做是数学中函数的计算,并且避免了状态以及变量的概念.wiki 我们知道,对象是面向对象的第一型,那么函数式编程也是一样,函数是函数 ...
随机推荐
- Linux的基本指令-Linux从入门到精通第二天(非原创)
文章大纲 一.指令与选项二.基础指令(重点)三.进阶指令(重点)四.学习资料下载五.参考文章 一.指令与选项 什么是Linux的指令?指在Linux终端(命令行)中输入的内容就称之为指令. 一 ...
- mysql单个表拆分成多个表
一.横向拆分 create table 新表的名称 select * from 被拆分的表 order by id limit int1,int2 int1为其实位置,int2为几条 注意:这样拆分后 ...
- 网络编程(四)--基于udp协议的套接字、socketserver模块
一.UDP协议(数据报协议) 1.何为udp协议 不可靠传输,”报头”部分一共只有8个字节,总长度不超过65,535字节,正好放进一个IP数据包. 以太网头 ip头 ...
- django url注册器组件, 响应器组件, 分页器组件
一.url注册器的使用 1.1导入模块 from django.urls import re_path, include from .serializer import views from rest ...
- 第02节-BLE协议各层的形象化理解
本篇博客根据韦大仙视频,整理所得. 先上框图: ATT层 从ATT开始看,在上篇博客讲的医院结构里面有个检验室,检验室可以得到各项结果,但是它并不知道这些结果代表什么含义.类比的在BLE协议栈里面,A ...
- 【oracle】ORA-00257 archiver error. Connect internal only, until freed
[原因]归档日志太多导致磁盘空间过小. [解决办法]删除日志或加大空间
- 修改MyEclipse/Eclipse左侧文字大小(MacOS/Windows)
一.Windows 首先找到 Eclipse/MyEclipse 的安装目录,然后找到如下目录: \plugins\org.eclipse.ui.themes_1.1.200.v20160815-05 ...
- ZROI 暑期高端峰会 A班 Day2 线性代数
高斯消元 很普及组,不讲了 当主元没有逆的时候可以辗转相除. 如果也没有带余数除法--没救了 逆矩阵 我们定义矩阵 \(A\) 的逆矩阵为 \(A^{-1}\),满足 \(AA^{-1}=A^{-1} ...
- 第08组 Beta冲刺(4/5)
队名:955 组长博客:点这里! 作业博客:点这里! 组员情况 组员1(组长):庄锡荣 过去两天完成了哪些任务 文字/口头描述 ? 测试新功能中 展示GitHub当日代码/文档签入记录 接下来的计划 ...
- R地图包 maps
安装 maps install.packages(“maps”) 使用 maps library("maps") nz <- map_data("nz")