如何为attrs字段添加验证器并正确进行类型注解?
attrs中NewType字段添加验证器后的mypy类型错误解决
问题重现
当使用NewType定义自定义类型,并在attrs frozen类中为字段添加正则验证器时,mypy会抛出类型不兼容错误:
初始无验证器的代码(mypy无报错):
from typing import NewType from attr import frozen, field UserId = NewType("UserId", str) @frozen class Order: id: UserId = field()
添加验证器后的代码(mypy报错):
from typing import NewType from attr import frozen, field, validators UserId = NewType("UserId", str) @frozen class Order: id: UserId = field(validator=validators.matches_re("^\d+$"))
mypy报错信息:
project/test_annotation.py:10: error: Incompatible types in assignment
(expression has type "str", variable has type "UserId") [assignment]Found 1 error in 1 file (checked 1 source file)
原因解释
这是因为:
validators.matches_re的类型标注默认关联str类型,mypy会据此推断field()的返回类型为str,而字段注解是基于str的自定义UserId(NewType),mypy不会自动将原始类型str转换为NewType子类。- 在attrs 22.x版本中,当给
field()指定验证器时,类型推断逻辑会优先匹配验证器的输入类型,而非字段的注解类型,最终导致类型不匹配。
解决办法
这里提供几种可行的方案:
方案1:给field指定明确的type参数
直接在field()中通过type参数声明字段类型为UserId,让mypy正确识别:
from typing import NewType from attr import frozen, field, validators UserId = NewType("UserId", str) @frozen class Order: id: UserId = field(validator=validators.matches_re("^\d+$"), type=UserId)
方案2:添加转换器自动转换类型
使用converter参数将输入的str自动转换为UserId类型,同时保留验证逻辑,运行时和静态检查都能正常工作:
from typing import NewType from attr import frozen, field, validators UserId = NewType("UserId", str) @frozen class Order: id: UserId = field( validator=validators.matches_re("^\d+$"), converter=UserId )
这个方案的优势是实例化Order时可以直接传入符合格式的字符串,无需手动用UserId()包裹。
方案3:使用cast强制类型转换
如果不想修改field参数,可以用typing.cast告诉mypy字段的实际类型:
from typing import NewType, cast from attr import frozen, field, validators UserId = NewType("UserId", str) @frozen class Order: id: UserId = cast(UserId, field(validator=validators.matches_re("^\d+$")))
内容的提问来源于stack exchange,提问作者devKanapka
相关产品推荐
相关产品推荐

