在数据类的自定义__init__中调用生成的__init__覆盖

问题描述

目前我有这样的东西:

@dataclass(frozen=True)
class MyClass:
  a: str
  b: str
  c: str
  d: Dict[str,str]

...除了dict都是可变的之外,这一切都很好,所以我不能使用我的班级来键入另一本字典。

相反,我希望字段d类似于FrozenSet[Tuple[str,str]],但我仍希望有人构造我的类的实例,以便能够在构造函数上传递字典这更加直观。

所以我想做类似的事情

@dataclass(frozen=True)
class MyClass:
  a: str
  b: str
  c: str
  d: FrozenSet[Tuple[str,str]] = field(init=False)

  def __init__(self,a,b,c,d: Dict[str,str]):
    self.original_generated_init(a,c)  # ???
    object.setattr(self,'d',frozenset(d.items()))  # required because my dataclass is frozen

我该如何实现?另外,有没有更优雅的方式来实现相同的目标?

解决方法

您可以使用InitVar并将其分配给__post_init__中的d

@dataclass(frozen=True)
class MyClass:
  a: str
  b: str
  c: str
  d: FrozenSet[Tuple[str,str]] = field(init=False)
  d_init: InitVar[Dict[str,str]]

  def __post_init__(self,d_init):
    object.__setattr__(self,'d',frozenset(d_init.items()))
,

a_guest给出的答案是正确的,并且与基本数据类一样好,因为您总是必须解决它们是can't support type-validation or -conversion设计的事实。如果您想干净地使用其中任何一个,则必须使用attrsmarshmallowpydantic之类的第三方库。

只是为了比较仅标准库的实现,我将向您展示pydantic中数据类的外观。这是一个相对较新的框架,与其他两个框架相比,它的历史意义要小得多:

from typing import FrozenSet,Tuple
from pydantic import dataclasses,validator


@dataclasses.dataclass(frozen=True)
class Foo:
    a: str
    b: str
    c: str
    d: FrozenSet[Tuple[str,str]]

    @validator('d',pre=True)
    def d_accepts_dicts(cls,v):
        """Custom validator that allows passing dicts as frozensets.
        
        Setting the 'pre' flag means that it will run before basic type
        validation takes place,e.g. pydantic will not raise a TypeError
        for passing a dict instead of something natively consistent,like for example a list,or a frozenset.
        The code itself only checks if the argument passed as 'd' quacks
        like a dict,and transforms it if the answer is 'yes'.
        """
        try:
            return frozenset(v.items())
        except AttributeError:
            return v

安装和使用另一个库会增加一些复杂性,但是如果您经常感到数据类需要我链接的初始列表(或pydantic的商标功能,运行时类型断言)中的某些内容,则可能值得它。