如何在Python中迭代数据类的属性?

M.w*_*wol 10 python iteration attributes class python-dataclasses

是否可以在Python中迭代数据类实例的属性?例如,我想在__post_init__double 整数属性中:

from dataclasses import dataclass, fields
@dataclass
class Foo:
    a: int
    b: int

    def __post_init__(self):
        self.double_attributes()

    def double_attributes(self):
        for field in fields(Foo):
            field = field*2

x = {
    'a': 1,
    'b': 2
}
y = Foo(**x)

>>> TypeError: unsupported operand type(s) for *: 'Field' and 'int'
Run Code Online (Sandbox Code Playgroud)

如何访问类实例的值并将其设置为如下所示但在循环中的其他值?

@dataclass
class Foo:
    a: int
    b: int

    def __post_init__(self):
        self.double_a()
        self.double_b()

    def double_a(self):
        self.a = self.a*2
    def double_b(self):
        self.b = self.b*2
Run Code Online (Sandbox Code Playgroud)

rv.*_*tch 13

您非常接近,但dataclasses.fields实际上返回了一个Field对象元组。至少在我的例子中,返回类型似乎没有正确注释,但这很容易修复。

from dataclasses import dataclass, fields, Field
from typing import Tuple


@dataclass
class Foo:
    a: int
    b: int

    def __post_init__(self):
        self.double_attributes()

    def double_attributes(self):

        # Note the annotation added here (tuple of one or more
        # `dataclasses.Field`s)
        cls_fields: Tuple[Field, ...] = fields(self.__class__)
        
        for field in cls_fields:

            # This check is to avoid fields annotated with other types
            # such as `str`
            if issubclass(field.type, int):
                new_val = getattr(self, field.name) * 2
                setattr(self, field.name, new_val)
Run Code Online (Sandbox Code Playgroud)

但是,如果您多次运行此操作(例如创建许多Foo对象),那么缓存整数字段列表可能会稍微高效一些。例如,以下是我可能建议的伪代码:

integer_fields: ClassVar[Frozenset[Field]] = frozenset(f for f in fields(cls) if issubclass(f.type, int))
Run Code Online (Sandbox Code Playgroud)