
本文介绍在 attrs 中定义支持 int | str 等联合类型的字段时,如何绕过 attrs.validators.or_() 引发的 mypy 类型推断错误,推荐使用 instance_of((str, int)) 这一简洁、安全且完全兼容的替代方案。
本文介绍在 attrs 中定义支持 `int | str` 等联合类型的字段时,如何绕过 `attrs.validators.or_()` 引发的 mypy 类型推断错误,推荐使用 `instance_of((str, int))` 这一简洁、安全且完全兼容的替代方案。
在使用 attrs 构建类型化数据类时,常需对字段进行运行时验证(例如允许 str 或 int)。官方文档中推荐的 attrs.validators.or_() 看似理想:
import attrs
@attrs.define
class C:
val: int | str = attrs.field(
validator=attrs.validators.or_(
attrs.validators.instance_of(int),
attrs.validators.instance_of(str),
)
)然而,mypy 会报错:error: Cannot infer type argument 1 of "or_" [misc]
这是因为 or_ 是一个泛型验证器,其内部类型推导在 mypy 当前版本(截至 2024)中尚不完善,无法从多个 instance_of(...) 调用中准确统一泛型参数。
✅ 推荐解决方案:使用元组形式的 instance_ofattrs.validators.instance_of 支持传入类型元组(如 (str, int)),语义等价于“是其中任一类型”,且完全被 mypy 正确识别:
import attrs
@attrs.define
class C:
val: int | str = attrs.field(
validator=attrs.validators.instance_of((str, int))
)
# ✅ mypy 无报错,运行时验证正常
c1 = C(42) # OK
c2 = C("hello") # OK
c3 = C(3.14) # Raises ValueError at runtime⚠️ 注意事项:
- 元组中的类型顺序无关紧要,
(int, str)与(str, int)效果一致; - 必须使用元组(
(...)),而非列表([...])或集合({...})——只有元组被instance_of显式支持; - 此方式在
attrs >= 23.2.0及主流 mypy(≥1.8)中稳定可用; - 若需更复杂的逻辑(如条件验证、自定义错误消息),仍可封装为
@attrs.validator方法,但简单类型校验优先用instance_of(tuple)。
? 总结:当目标是验证字段是否属于多个具体类型之一时,instance_of((T1, T2, ...)) 是比 or_(instance_of(T1), instance_of(T2)) 更简洁、更健壮、且类型友好的首选实践。它保持了零运行时开销、清晰语义和完整静态类型安全性。

















