在数据类上的自定义 `__init__` 覆盖中调用生成的 `__init__`
Calling generated `__init__` in custom `__init__` override on dataclass
目前我有这样的东西:
@dataclass(frozen=True)
class MyClass:
a: str
b: str
c: str
d: Dict[str, str]
...一切都很好,除了 dict
是可变的,所以我不能用我的 class 来键入另一个字典。
相反,我希望字段 d
类似于 FrozenSet[Tuple[str, str]]
,但我仍然希望有人构建我的 class 的实例以便能够通过关于构造函数的字典,因为这更直观。
所以我想做一些类似的事情
@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, b, c) # ???
object.setattr(self, 'd', frozenset(d.items())) # required because my dataclass is frozen
我该如何实现?或者有没有更优雅的方法来实现同样的事情?
您可以使用 InitVar
and assign to d
in __post_init__
:
@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 by design. If you want to use either of that cleanly, you have to use a third-party library like attrs, marshmallow, or pydantic.
为了比较仅标准库的实现,我将向您展示您的数据类在 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 的商标功能,运行时类型断言),它可能很好值得。
目前我有这样的东西:
@dataclass(frozen=True)
class MyClass:
a: str
b: str
c: str
d: Dict[str, str]
...一切都很好,除了 dict
是可变的,所以我不能用我的 class 来键入另一个字典。
相反,我希望字段 d
类似于 FrozenSet[Tuple[str, str]]
,但我仍然希望有人构建我的 class 的实例以便能够通过关于构造函数的字典,因为这更直观。
所以我想做一些类似的事情
@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, b, c) # ???
object.setattr(self, 'd', frozenset(d.items())) # required because my dataclass is frozen
我该如何实现?或者有没有更优雅的方法来实现同样的事情?
您可以使用 InitVar
and assign to d
in __post_init__
:
@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 by design. If you want to use either of that cleanly, you have to use a third-party library like attrs, marshmallow, or pydantic.
为了比较仅标准库的实现,我将向您展示您的数据类在 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 的商标功能,运行时类型断言),它可能很好值得。