page contents

voluptuous,一个非常实用的 Python 库!

Python 项目里很多脏活,不是算法问题,是数据进来时就已经歪了:配置文件少字段、接口参数类型不对、CSV 导入空字符串、JSON 里数字被前端当成字符串传过来。

attachments-2026-07-wE7INTqB6a5ed0f7c6977.png接口又被一个空字符串打穿了。

日志里不是数据库报错,也不是 Redis 超时,是这种东西:

payload={"shop_id":"1024","amount":"", "items":[{"sku":"A19","qty":"2"}]}
error=invalid literal for int() with base 10: ''

这种问题我第一眼一般不去翻业务代码。先看入口参数。

Python 项目里很多脏活,不是算法问题,是数据进来时就已经歪了:配置文件少字段、接口参数类型不对、CSV 导入空字符串、JSON 里数字被前端当成字符串传过来。

你在业务代码里到处写:

if "shop_id" not in data:
    ...
if not isinstance(data["items"], list):
    ...
if int(data["qty"]) <= 0:
    ...

写两天还能忍,写两个月就开始恶心。

这种活,我一般会丢给 voluptuous。

它不是那种很重的框架,也不抢你的项目结构,就是一个数据校验库。你给它一份规则,它帮你把数据检查一遍,能转类型就转,转不了就把错误吐出来。

安装就一行:

pip install voluptuous

先看一个接口入参的场景。

订单创建接口收到的 JSON 大概长这样:

raw_order = {
    "shop_id": "1024",
    "amount": "39.90",
    "items": [
        {"sku": "A19", "qty": "2"},
        {"sku": "B07", "qty": 1}
    ],
    "remark": ""
}

这种数据很常见。前端传来的数字不一定真是数字,导入脚本传来的字段也不一定干净。

用 voluptuous 我会这么写:

from decimal import Decimal, InvalidOperation

from voluptuous import Schema, Required, Optional, All, Length, Range, Coerce, Invalid


def to_money(value):
    try:
        money = Decimal(str(value)).quantize(Decimal("0.01"))
    except (InvalidOperation, TypeError):
        raise Invalid("amount 不是合法金额")

    if money <= 0:
        raise Invalid("amount 必须大于 0")

    return money


order_schema = Schema({
    Required("shop_id"): All(Coerce(int), Range(min=1)),
    Required("amount"): to_money,
    Required("items"): All([
        {
            Required("sku"): All(str, Length(min=1, max=32)),
            Required("qty"): All(Coerce(int), Range(min=1, max=999)),
        }
    ], Length(min=1)),
    Optional("remark", default=""): All(str, Length(max=200)),
}, required=True)

order = order_schema(raw_order)

print(order)

处理完以后,shop_id 会变成 int,amount 会变成 Decimal,qty 也会变成 int。

我喜欢这个地方。

它不是只告诉你“错了”,它还顺手把能修正的数据类型修正掉。后面的业务代码就干净很多,不用每走一步都担心字段是不是字符串。

当然,别什么都让它自动转。

比如手机号、身份证、订单号这种东西,我一般不做 Coerce(int)。前导零一丢,后面查问题能把人看烦。

还有一种场景更适合它:配置校验。

很多 Python 脚本刚开始都是随手写的,配置长这样:

sync_conf = {
    "source": "mysql",
    "batch_size": "500",
    "retry": 3,
    "fields": ["user_id", "phone", "created_at"],
    "mode": "append"
}

脚本小的时候,配置错了大不了重跑。

等它挂在定时任务里,每天凌晨跑同步,配置错一次,第二天早上就有人问你为什么数据少了一截。

我一般会在启动时先校验:

from voluptuous import Any, In

sync_schema = Schema({
    Required("source"): In(["mysql", "postgres", "csv"]),
    Required("batch_size"): All(Coerce(int), Range(min=100, max=5000)),
    Optional("retry", default=2): All(Coerce(int), Range(min=0, max=5)),
    Required("fields"): All([All(str, Length(min=1))], Length(min=1)),
    Optional("mode", default="append"): Any("append", "replace"),
}, required=True)


def load_sync_job(conf: dict) -> dict:
    try:
        return sync_schema(conf)
    except Exception as e:
        print(f"[config-check-failed] reason={e} conf={conf}")
        raise

这段代码没什么花活,但很值。

因为它把问题拦在脚本启动阶段,而不是跑到一半写了三张表以后才炸。

voluptuous 的错误信息也还可以看。

比如把 batch_size 改成 20,大概会看到类似这种:

value must be at least 100 for dictionary value @ data['batch_size']

虽然不算特别优雅,但定位够了。线上排障最怕的不是错误丑,是错误绕。

再说一个我踩过比较多的点。

required=True 要不要开?

我一般在最外层开:

Schema({...}, required=True)

意思是规则里写的字段默认都必须有。否则你很容易以为某个字段被校验了,实际它没传也能过。

但里面一些可选字段,要显式用 Optional:

Optional("remark", default="")

这样读代码的人一眼就知道:这个字段可没有,也有默认值。

还有列表校验,也别写散。

比如导入用户:

user_rows_schema = Schema([
    {
        Required("mobile"): All(str, Length(min=11, max=11)),
        Required("name"): All(str, Length(min=1, max=30)),
        Optional("age"): All(Coerce(int), Range(min=1, max=120)),
    }
])

这东西拿来处理 Excel、CSV 导入挺舒服。

但我会加一层行号,不然用户只知道“某行错了”,不知道哪一行:

def check_rows(rows):
    good_rows = []
    for line_no, row in enumerate(rows, start=2):
        try:
            good_rows.append(user_rows_schema([row])[0])
        except Exception as e:
            print(f"[import-row-invalid] line={line_no} reason={e} row={row}")
    return good_rows

这里我故意没有一错就停。

导入类任务和接口不一样。接口错了直接返回,导入任务最好把错误行全扫出来,不然用户改一行传一次,来回折腾。

voluptuous 还有一个好处是,它不会逼你换项目模型。

不像有些库,一上来让你定义一堆 class,字段、注解、继承全安排好。你要是写大型接口服务,那样当然也行。

但很多 Python 活不是那种项目。

就是一个脚本,一个内部接口,一个数据清洗任务,一个临时迁移程序。这个时候我不想引入太重的东西。voluptuous 刚好卡在一个舒服的位置:规则集中,代码不散,侵入也小。

不过它也不是万能的。

复杂对象建模、自动生成文档、和 Web 框架深度绑定,这些不是它最强的地方。要搞很完整的 API schema,可能会选别的方案。

但在我这里,它最适合干三件事:

接口入口兜底。

配置启动前检查。

导入数据先洗一遍。

脏数据别往业务代码里放。入口处能拦住,就别指望后面每个函数都小心翼翼。后面的人不会那么小心,你自己过两周也不会。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2247 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. Pack 2247 文章
  3. 小柒 2228 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章