据我所知,attrs
没有内置的选项来将转换或验证切换到"元素方式",就像Pydtic的验证器有each_item
参数那样.
我知道您没有明确要求一个转换器函数,但我真的不认为定义一个您可以根据需要经常重用的函数有多大问题.以下是为您的特定情况实现转换器的一种方法:
from attrs import define, field
from collections.abc import Iterable
from typing import Any
def float_list(iterable: Iterable[Any]) -> list[float]:
return [float(item) for item in iterable]
@define
class A:
values: list[float] = field(converter=float_list)
if __name__ == '__main__':
a = A(values=["1.1", "2.2", "3.3"])
print(a)
这与您使用converter=float
的示例没有太大区别.
输出当然是A(values=[1.1, 2.2, 3.3])
.
您甚至可以拥有自己的通用转换器工厂,用于任意可转换项类型:
from attrs import define, field
from collections.abc import Callable, Iterable
from typing import Any, TypeAlias, TypeVar
T = TypeVar("T")
ItemConv: TypeAlias = Callable[[Any], T]
ListConv: TypeAlias = Callable[[Iterable[Any]], list[T]]
def list_of(item_type: ItemConv[T]) -> ListConv[T]:
def converter(iterable: Iterable[Any]) -> list[T]:
return [item_type(item) for item in iterable]
return converter
@define
class B:
foo: list[float] = field(converter=list_of(float))
bar: list[int] = field(converter=list_of(int))
baz: list[bool] = field(converter=list_of(bool))
if __name__ == '__main__':
b = B(
foo=range(0, 10, 2),
bar=["1", "2", 3.],
baz=(-1, 0, 100),
)
print(b)
输出:B(foo=[0.0, 2.0, 4.0, 6.0, 8.0], bar=[1, 2, 3], baz=[True, False, True])
这种方法唯一的缺点是attrs
的mypy
插件(由于某些原因)不能处理这种类型的转换器功能,并且会出现错误,除非您在有问题的字段定义中添加# type: ignore[misc]
.