假设有以下情况:

import attrs

@attrs.define(kw_only=True)
class A:
    values: list[float] = attrs.field(converter=float)

A(values=["1.1", "2.2", "3.3"])

这导致了

*** TypeError: float() argument must be a string or a real number, not 'list'

显然,这是因为将整个列表提供给float,但有没有方法可以让attrs对每个元素执行转换,而不提供自定义的转换函数?

推荐答案

据我所知,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])

这种方法唯一的缺点是attrsmypy插件(由于某些原因)不能处理这种类型的转换器功能,并且会出现错误,除非您在有问题的字段定义中添加# type: ignore[misc].

Python-3.x相关问答推荐

类型的可变性对变量的作用域有影响吗?

Python3和请求-超文本标记语言:试图抓取一个网站-没有取回真正的超文本标记语言代码

如何从包含SPAN文本的标记中获取链接

它们是否同样存储在python3的内存中?

转换Pandas 数据框 - 添加行

txt 文件与不同的分隔符到整数列表

numpy是如何添加@运算符的?

有没有办法使用 python opencv 计算与图像的白色距离

Python defaultdict 在获取时返回 None,尽管使用默认值初始化

Tkinter AttributeError:对象没有属性'tk'

pysftp vs. Paramiko

Python 解包运算符 (*)

Linux Mint 上的 Python3 错误没有名为蓝牙的模块

使用 Tensorflow 2.0 在 MNIST 上实现自定义神经网络?

谁能给我一个 Python 3 中标准输入和标准输出的快速教程?

在 Alembic 迁移期间更新列内容

带有 Emacs 的 Python 3

什么是ANSI_X3.4-1968编码?

在动态链接库 Anaconda3\Library\bin\mkl_intel_thread.dll 中找不到序数 242

在 PostgreSQL 上使用 SQLAlchemy 创建全文搜索索引