迭代数据类键和值的更好方法是什么?

Adi*_*n D 40 python python-dataclasses

我有两个数据类,Route并且Factors. Route包含一个值和三个副本Factors

Route不知道Factors包含多少个变量。我想获取这些变量的名称,然后获取Factors.

这是我目前拥有的:

@dataclass
class Factors:
    do: bool  # does it do the route
    hub: int # how many of the locations are hubs

    def __init__(self, do_init):
        self.do = do_init
        self.hub = 0 # will add later 

    def __str__(self):
        return "%s" % self.do


@dataclass
class Route:
    route: tuple
    skyteam: Factors
    star: Factors
    oneworld: Factors

    def __init__(self, route):
        self.route = route.get('route')
        # this could probably be done with one line loop and a variable with names
        self.skyteam = Factors(route.get('skyteam'))
        self.star = Factors(route.get('star'))
        self.oneworld = Factors(route.get('oneworld'))

    def __str__(self):
        table = [[self.route, "SkyTeam", "StarAlliance", "OneWorld"]] # var name is fine
        for var in Factors.__dict__.get('__annotations__').keys():  # for each factor
            factor = [var]
            factor.append(self.skyteam.__dict__.get(var))
            factor.append(self.star.__dict__.get(var))
            factor.append(self.oneworld.__dict__.get(var))
            table.append(factor)
        return tabulate.tabulate(table, tablefmt='plain')
Run Code Online (Sandbox Code Playgroud)

输入是

{'route': ('BOS', 'DXB'), 'skyteam': True, 'star': True, 'oneworld': True}
Run Code Online (Sandbox Code Playgroud)

电流输出为

@dataclass
class Factors:
    do: bool  # does it do the route
    hub: int # how many of the locations are hubs

    def __init__(self, do_init):
        self.do = do_init
        self.hub = 0 # will add later 

    def __str__(self):
        return "%s" % self.do


@dataclass
class Route:
    route: tuple
    skyteam: Factors
    star: Factors
    oneworld: Factors

    def __init__(self, route):
        self.route = route.get('route')
        # this could probably be done with one line loop and a variable with names
        self.skyteam = Factors(route.get('skyteam'))
        self.star = Factors(route.get('star'))
        self.oneworld = Factors(route.get('oneworld'))

    def __str__(self):
        table = [[self.route, "SkyTeam", "StarAlliance", "OneWorld"]] # var name is fine
        for var in Factors.__dict__.get('__annotations__').keys():  # for each factor
            factor = [var]
            factor.append(self.skyteam.__dict__.get(var))
            factor.append(self.star.__dict__.get(var))
            factor.append(self.oneworld.__dict__.get(var))
            table.append(factor)
        return tabulate.tabulate(table, tablefmt='plain')
Run Code Online (Sandbox Code Playgroud)

也许我可以搜索Route包含Factors数据类型的每个变量并迭代这些变量?

And*_*bov 65

您可以使用dataclass.fields

from dataclasses import dataclass, fields

for field in fields(YourDataclass):
    print(field.name, getattr(YourDataclass, field.name))
Run Code Online (Sandbox Code Playgroud)

  • 我总是感到惊讶的是,我们必须使用“getattr”,而且他们没有计划通过“field.value”这样的简单属性进行访问。 (3认同)

Ian*_*dby 14

可能最简单的方法是 import asdictfrom dataclasses,然后编写如下内容:

for key, value in asdict(route).items():
    ...
Run Code Online (Sandbox Code Playgroud)

对于小型数据类,将数据类对象复制到新字典的成本比便利性和可读性更重要。


Azi*_*lto 8

我还会使用它__dataclass_fields__返回变量名称及其类型的字典。例如:

for field in mydataclass.__dataclass_fields__:
    value = getattr(mydataclass, field)
    print(field, value)
Run Code Online (Sandbox Code Playgroud)

  • 这有效,但似乎没有记录。不过,它与 dataclasses.fields() 返回的属性相同。可能使用后者更好。 (3认同)

Arn*_*rne 6

我会保留内置__str__函数,只调用类中的函数visualize或其他内容Route,但这就是品味。__init__除非绝对必要,否则您也不应该重载数据类的 ,只需将输入字典放入默认构造函数即可。

最后一点,尝试使用getattr/setattr过度访问__dict__数据类,它很流行用于__slots__存储其属性,这会以一种不平凡的方式破坏您的代码。

所以我会采用这样的方法,使用tabulate库来处理渲染:

from dataclasses import dataclass, fields
import tabulate

@dataclass
class Factor:
    do: bool
    hub: int = 0 # this is how to add a default value


@dataclass
class Route:
    route: tuple
    skyteam: Factor
    star: Factor
    oneworld: Factor

    def __post_init__(self):
        # turn Factor parameter dicts into Factor objects
        for field in fields(self):
            if issubclass(field.type, Factor):
                setattr(self, field.name, field.type(getattr(self, field.name)))

    def visualize(self):
        factors = {
            field.name: getattr(self, field.name)
            for field in fields(self)
            if issubclass(field.type, Factor)
        }
        rows = [[self.route, *factors]]  # header
        for field in fields(Factor):
            rows.append([field.name, *[getattr(f, field.name) for f in factors.values()]])
        print(tabulate.tabulate(rows))
Run Code Online (Sandbox Code Playgroud)

这对于你的例子来说效果很好:

>>> r = Route(**{'route': ('BOS', 'DXB'), 'skyteam': True, 'star': True, 'oneworld': True})
>>> r.visualize()
--------------  -------  ----  --------
('BOS', 'DXB')  skyteam  star  oneworld
do              True     True  True
hub             0        0     0
--------------  -------  ----  --------
Run Code Online (Sandbox Code Playgroud)

如果您向 Factor 类添加更多字段并向 Route 添加更多因子实例,则此解决方案应该继续有效。