不要手动做数据校验
在日常编码时,很大比例的错误处理工作和用户输入有关。当程序里的某些数据直接来自用户输入时,我们必须先校验这些输入值,再进行之后的处理,否则就会出现难以预料的错误。
举例来说,写一个命令行小程序,它要求用户输入一个0~100范围的数字。假如用户输入的内容无效,就要求其重新输入。要求用户输入数字的脚本(手动校验)。
小程序的代码如代码清单所示:
def input_a_number():
"""要求用户输入一个0~100的数字,如果无效则重新输入"""
while 1:
number = input('Please input a number (0-100): ')
# 下面的三条if语句都是对输入值的校验代码
if not number:
print('Input can not be empty!')
continue
if not number.isdigit():
print('Your input is not be a valid number!')
continue
if not (0 <= int(number) <= 100):
print('Please input a number between 0 and 100!')
continue
number = int(number)
break
print(f'Your number is {number}')
input_a_number()
运行效果如下:
Please input a number (0-100):
Input can not be empty!
Please input a number (0-100): foo
Your input is not be a valid number!
Please input a number (0-100): 65
Your number is 65
这个函数共包含14行代码,其中9行if都在校验数据。也行你觉得这样的代码结构很正常,但请想象一下,假如我们需要校验的输入不止一个,校验逻辑也比这个复杂怎么办?
如何改进这段代码呢?假如把数据校验代码抽成一个独立函数,和核心逻辑隔离开,代码肯定会变得更清晰。不过比这更重要的是,我们要把“输入数据校验”当作一个独立的领域,挑选更适合的模块来完成这项工作。
在数据校验这块,pydantic模块是一个不错的选择。如果用它来做校验,上面的代码可以改成如下形式:
**要求用户输入数字的脚步(使用pydantic库)**
from pydantic import BaseModel, conint, ValidationError
class NumberInput(BaseModel):
number: conint(gt=0, le=100)
def input_a_number_with_pydantic():
while 1:
number = input('Please input a number (0-100):')
# 实例化pydantic模型,捕获校验错误异常
try:
number_input = NumberInput(number=number)
except ValidationError as e:
print(e)
continue
number = number_input.number
break
print(f'Your number is {number}')
input_a_number_with_pydantic()
使用专业的数据校验模块后,整段代码变得简单了许多。在编写代码时,我们应当尽量避免手动校验任何数据。因为数据校验任务独立性很强,所以应该引入合适的第三方校验模块(或者自己实现),让它们来处理这部分专业工作。