如何在冻结的数据类自定义 __init__ 方法中设置属性?

新手上路,请多包涵

我正在尝试构建一个 @dataclass 它定义了一个模式,但实际上并没有用给定的成员实例化。 (基本上,我劫持了方便的 @dataclass 语法用于其他目的)。这几乎可以满足我的要求:

 @dataclass(frozen=True, init=False)
class Tricky:
    thing1: int
    thing2: str

    def __init__(self, thing3):
        self.thing3 = thing3

但是我在 __init__ 方法中得到了 FrozenInstanceError --- :

 dataclasses.FrozenInstanceError: cannot assign to field 'thing3'

我需要 frozen=True (用于散列性)。有什么方法可以在冻结的 --- 中设置自定义属性 __init__ @dataclass

原文由 Sasgorilla 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 935
1 个回答

问题是 默认的 __init__ 实现使用 object.__setattr__() 和冻结类,通过提供你自己的实现,你也必须使用它,这会使你的代码非常 hacky:

 @dataclass(frozen=True, init=False)
class Tricky:
    thing1: int
    thing2: str

    def __init__(self, thing3):
        object.__setattr__(self, "thing3", thing3)

不幸的是,python 不提供使用默认实现的方法,因此我们不能简单地执行以下操作:

 @dataclass(frozen=True, init=False)
class Tricky:
    thing1: int
    thing2: str

    def __init__(self, thing3, **kwargs):
        self.__default_init__(DoSomething(thing3), **kwargs)

但是,我们可以很容易地实现该行为:

 def dataclass_with_default_init(_cls=None, *args, **kwargs):
    def wrap(cls):
        # Save the current __init__ and remove it so dataclass will
        # create the default __init__.
        user_init = getattr(cls, "__init__")
        delattr(cls, "__init__")

        # let dataclass process our class.
        result = dataclass(cls, *args, **kwargs)

        # Restore the user's __init__ save the default init to __default_init__.
        setattr(result, "__default_init__", result.__init__)
        setattr(result, "__init__", user_init)

        # Just in case that dataclass will return a new instance,
        # (currently, does not happen), restore cls's __init__.
        if result is not cls:
            setattr(cls, "__init__", user_init)

        return result

    # Support both dataclass_with_default_init() and dataclass_with_default_init
    if _cls is None:
        return wrap
    else:
        return wrap(_cls)

接着

@dataclass_with_default_init(frozen=True)
class DataClass:
    value: int

    def __init__(self, value: str):
        # error:
        # self.value = int(value)

        self.__default_init__(value=int(value))

_更新_:我打开 了这个 错误,我希望在 3.9 之前实现它。

原文由 Shmuel H. 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题