如何在冻结的数据类自定义 __init__ 方法中设置属性? [英] How can I set an attribute in a frozen dataclass custom __init__ method?
问题描述
我正在尝试构建一个 @dataclass
来定义架构,但实际上并未使用给定成员进行实例化.(基本上,我为了其他目的劫持了方便的 @dataclass
语法).这几乎就是我想要的:
I'm trying to build a @dataclass
that defines a schema but is not actually instantiated with the given members. (Basically, I'm hijacking the convenient @dataclass
syntax for other purposes). This almost does what I want:
@dataclass(frozen=True, init=False)
class Tricky:
thing1: int
thing2: str
def __init__(self, thing3):
self.thing3 = thing3
但是我在 __init__
方法中得到一个 FrozenInstanceError
:
But I get a FrozenInstanceError
in the __init__
method:
dataclasses.FrozenInstanceError: cannot assign to field 'thing3'
我需要 frozen=True
(为了可散列性).有什么方法可以在冻结的 @dataclass
上的 __init__
中设置自定义属性?
I need the frozen=True
(for hashability). Is there some way I can set a custom attribute in __init__
on a frozen @dataclass
?
推荐答案
问题在于 默认的__init__
实现使用带有冻结类的object.__setattr__()
并且通过提供您自己的实现,您也必须使用它使您的代码变得非常笨拙:
The problem is that the default __init__
implementation uses object.__setattr__()
with frozen classes and by providing your own implementation, you have to use it too which would make your code pretty hacky:
@dataclass(frozen=True, init=False)
class Tricky:
thing1: int
thing2: str
def __init__(self, thing3):
object.__setattr__(self, "thing3", thing3)
不幸的是,python 没有提供使用默认实现的方法,因此我们不能简单地执行以下操作:
Unfortunately, python does not provide a way to use the default implementation so we can't simply do something like:
@dataclass(frozen=True, init=False)
class Tricky:
thing1: int
thing2: str
def __init__(self, thing3, **kwargs):
self.__default_init__(DoSomething(thing3), **kwargs)
然而,我们可以很容易地实现这种行为:
However, with we can implement that behavior quite easily:
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.
Update: I opened this bug and I hope to implement that by 3.9.
这篇关于如何在冻结的数据类自定义 __init__ 方法中设置属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!