【问题标题】:What is a better way to iterate over dataclass keys and values?迭代python数据类键和值的更好方法?
【发布时间】:2020-06-28 00:31:33
【问题描述】:

我有两个数据类,Route 和 Factors。 Route 包含一个值和三个 Factors 副本。 Route 不知道 factor 包含多少变量。我想获取这些变量的名称,然后为每个 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')

输入是

{'route': ('BOS', 'DXB'), 'skyteam': True, 'star': True, 'oneworld': True}

当前输出为

('BOS', 'DXB')  SkyTeam  StarAlliance  OneWorld
do              True     True          True
hub             0        0             0

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

【问题讨论】:

  • 我来到这里是因为我也希望tabulate 支持数据类作为行。

标签: python python-dataclasses


【解决方案1】:

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

for field in mydataclass.__dataclass_fields__:
    value = getattr(mydataclass, field)
    print(field, value)

【讨论】:

    【解决方案2】:

    您可以使用dataclass.fields

    from dataclasses import dataclass, fields
    
    field in fields(YourDataclass):
        print(field.name, getattr(YourDataclass, field.name))
    

    【讨论】:

      【解决方案3】:

      我会不理会内置的 __str__s,只调用函数 visualizeRoute 类上的东西,但这就是品味。您也不应该重载数据类的__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))
      

      这对你的例子来说很好:

      >>> 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
      --------------  -------  ----  --------
      

      如果您将更多字段添加到 Factor 类并将更多因子实例添加到 Route,此解决方案应该会继续工作。

      【讨论】:

      • 可以*[f for f in factors]*factors吗?
      • @wuyudi 绝对可以,那条线是早期版本的神器。谢谢!
      【解决方案4】:

      要访问类本身的字段,这是我尝试过的。

      from dataclass import dataclass, fields
      
      @dataclass
      class someDataclass:
          def access_fields(self):
              for field in fields(self.__class__):
                  print(field) # you get the Field() instances
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-12-14
        • 1970-01-01
        • 2021-10-24
        • 2020-12-06
        • 1970-01-01
        • 2021-09-10
        • 1970-01-01
        相关资源
        最近更新 更多