如何使用attrs验证Literal类型或None的实例?
规范解决方案:用attrs内置验证器处理Literal|None类型验证
原代码的核心问题是instance_of验证器仅支持运行时存在的类/类型,而Literal是静态类型提示,不属于运行时可实例化的类,因此直接传入Literal[OK_ARGS]会触发Subscripted generics cannot be used with class and instance checks错误。同时注意:类装饰器应该用@frozen或@define而非@field,原代码存在装饰器误用问题。
以下是两种规范的实现方式:
方式一:结合optional与in_内置验证器(推荐)
Literal['a','b']的本质是限制值必须在指定列表内,因此直接用in_验证器检查值范围,再用optional处理None的情况,完全匹配Literal|None的类型约束:
from attrs import frozen, field from attrs.validators import optional, in_ from typing import Literal OK_ARGS = ['a', 'b'] @frozen class MyClass: my_field: Literal['a', 'b'] | None = field(validator=optional(in_(OK_ARGS)))
optional验证器会自动跳过None值的检查;- 当值非
None时,in_(OK_ARGS)会验证值是否在允许的列表中,错误提示符合attrs的标准格式。
方式二:从Literal类型中自动提取允许值
如果希望验证逻辑与类型提示完全绑定(修改Literal时无需手动同步OK_ARGS),可以用typing.get_args提取Literal的参数:
from attrs import frozen, field from attrs.validators import optional, in_ from typing import Literal, get_args @frozen class MyClass: # 定义Literal类型 MyFieldType = Literal['a', 'b'] | None my_field: MyFieldType = field( # 从Literal中提取允许的非None值 validator=optional(in_(get_args(Literal['a', 'b']))), )
或者将允许值提前提取,让代码更清晰:
ALLOWED_VALUES = get_args(Literal['a', 'b']) @frozen class MyClass: my_field: Literal['a', 'b'] | None = field(validator=optional(in_(ALLOWED_VALUES)))
对比自定义验证器的优势
- 复用attrs内置验证器,代码更简洁、可读性更强;
- 错误提示标准化(attrs内置验证器会输出统一格式的错误信息);
- 无需手动编写类型检查逻辑,降低维护成本。
内容的提问来源于stack exchange,提问作者GlaceCelery
相关产品推荐
相关产品推荐

