【问题标题】:Passing helper variables to dataclass __init__ without assigning them将辅助变量传递给数据类 __init__ 而不分配它们
【发布时间】:2020-02-18 23:05:03
【问题描述】:

我有一个根级 config 类,我通过依赖注入将其传递到我的代码库中。

问题是,我有这个数据类需要这个配置中的一些属性来计算 __post_init__() 中的值 world_coords

为了保持我的测试干净并规避其他测试导入问题(此处未讨论),我希望能够将配置直接传递给该对象,而不是从导入中读取这些值。但是,如果我将配置构造为参数,它就会成为数据类的一个属性,这是我试图避免的。 RoadSegmentNode 确实不需要在使用后保留对配置的引用。

这是类的基本结构:

@dataclass(eq=True, frozen=True)  # make hashable
class RoadSegmentNode:

    tile_index: Tuple[int, int]  # (r, c)
    dir: Direction
    node_type: RoadNodeType
    world_coords: Tuple[int, int] = field(init=False)

    def __post_init__(self):

        # (Use config variables here, e.g. `config.TILE_WIDTH`, to calculate x and y)

        # Hack to get around frozen=True. We don't care that we're mutating
        # an "immutable" object on __init__().
        object.__setattr__(self, "world_coords", (x, y))

这是我使用依赖注入模型来暂时解除测试阻塞的权宜之计。注意RoadSegmentNode 现在有一堆只用于初始化的新属性。这比保留对配置的引用要好一些,因为至少它们是明确的,但它仍然是一个很糟糕的设计。

@dataclass(eq=True, frozen=True)  # make hashable
class RoadSegmentNode:

    # NOTE: DO NOT ACCESS THESE ATTRIBUTES!
    grid_width: int
    grid_height: int
    tile_width: int
    tile_height: int
    road_width: int

    tile_index: Tuple[int, int]  # (r, c)
    dir: Direction
    node_type: RoadNodeType
    world_coords: Tuple[int, int] = field(init=False)

    def __post_init__(self):

        # (Use attributes here, e.g. `self.tile_width`, to calculate x and y)

        # Hack to get around frozen=True. We don't care that we're mutating
        # an "immutable" object on __init__().
        object.__setattr__(self, "world_coords", (x, y))

如何将配置传递给数据类进行初始化而不使其成为数据类的属性?我是否应该为这个用例考虑一个数据类?我相信最初的意图是保持所有实例不可变,但我无法确认。

【问题讨论】:

  • 不将config 定义为init-only variable 解决您的问题吗?
  • @Tupteq 这看起来正是我所需要的!我在文档中完全错过了这一点。回到电脑前,我将需要对其进行测试。
  • @Tupteq 完美运行。如果您将其作为问题的官方答案,我可以接受。
  • 很高兴我能帮上忙。我会回答的。

标签: python python-3.x python-dataclasses


【解决方案1】:

您应该将config 定义为init-only variable。这样它会被传递给__post_init__(),但之后会消失:

from dataclass import dataclass, field
from typing import InitVar

@dataclass(eq=True, frozen=True)
class RoadSegmentNode:

    tile_index: Tuple[int, int]
    dir: Direction
    node_type: RoadNodeType
    world_coords: Tuple[int, int] = field(init=False)
    config: InitVar[Config]  # will not appear in instances


    def __post_init__(self, config):
        x, y = ..., ...  # calculate using config

        # looks hacky for sure, but is the right way to work with frozen dataclasses
        object.__setattr__(self, "world_coords", (x, y))

【讨论】:

  • 对于 3.8 及更高版本,使用 import InitVar from dataclass,而不是输入。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-23
  • 2019-05-05
  • 1970-01-01
  • 1970-01-01
  • 2013-01-16
相关资源
最近更新 更多