如何使非冻结数据类冻结,反之亦然?
How to make non-frozen dataclass frozen, and vice versa?
我想知道任何简单的方法来冻结下面的数据类bar
。
@dataclass
class Bar:
foo: int
bar = Bar(foo=1)
换句话说,我想要下面的函数some_fn_to_freeze
frozen_bar = some_fn_to_freeze(bar)
frozen_bar.foo = 2 # Error
并且,反函数some_fn_to_unfreeze
bar = som_fn_to_unfrozen(frozen_bar)
bar.foo = 3 # not Error
dataclass
没有 built-in 支持。 Frozen-ness 是在 class-wide 的基础上进行跟踪的,而不是 per-instance,并且不支持自动生成冻结或未冻结的数据类等价物。
虽然您可以尝试做一些事情来即时生成新的数据类,但它与 isinstance
、==
以及您想要工作的其他东西的交互非常差。只写两个数据类和转换器方法可能更安全:
@dataclass
class Bar:
foo: int
def as_frozen(self):
return FrozenBar(self.foo)
@dataclass(frozen=True)
class FrozenBar:
foo: int
def as_unfrozen(self):
return Bar(self.foo)
Python 数据类很棒,但如果您能够使用 third-party 库,attrs 包是更灵活的选择。例如:
import attr
# Your class of interest.
@attr.s()
class Bar(object):
val = attr.ib()
# A frozen variant of it.
@attr.s(frozen = True)
class FrozenBar(Bar):
pass
# Three instances:
# - Bar.
# - FrozenBar based on that Bar.
# - Bar based on that FrozenBar.
b1 = Bar(123)
fb = FrozenBar(**attr.asdict(b1))
b2 = Bar(**attr.asdict(fb))
# We can modify the Bar instances.
b1.val = 777
b2.val = 888
# Check current vals.
for x in (b1, fb, b2):
print(x)
# But we cannot modify the FrozenBar instance.
try:
fb.val = 999
except attr.exceptions.FrozenInstanceError:
print(fb, 'unchanged')
输出:
Bar(val=888)
FrozenBar(val=123)
Bar(val=999)
FrozenBar(val=123) unchanged
改变 frozen
数据类的标准方法是使用 dataclasses.replace
:
old_bar = Bar(foo=123)
new_bar = dataclasses.replace(old_bar, foo=456)
assert new_bar.foo == 456
对于更复杂的 use-cases,您可以使用来自的数据类实用程序模块:https://github.com/google/etils
它添加了一个 my_dataclass = my_dataclass.unfrozen()
成员,允许直接改变 frozen
数据类
# pip install etils[edc]
from etils import edc
@edc.dataclass(allow_unfrozen=True) # Add the `unfrozen()`/`frozen` method
@dataclasses.dataclass(frozen=True)
class A:
x: Any = None
y: Any = None
old_a = A(x=A(x=A()))
# After a is unfrozen, the updates on nested attributes will be propagated
# to the top-level parent.
a = old_a.unfrozen()
a.x.x.x = 123
a.x.y = 'abc'
a = a.frozen() # `frozen()` recursively call `dataclasses.replace`
# Only the `unfrozen` object is mutated. Not the original one.
assert a == A(x=A(x=A(x = 123), y='abc'))
assert old_a == A(x=A(x=A()))
如示例所示,您可以 return unfrozen
/frozen
数据类的副本,它明确设计用于改变嵌套数据类。
@edc.dataclass
还向数据类添加一个 a.replace(**kwargs)
方法(dataclasses.dataclass
的别名)
a = A()
a = a.replace(x=123, y=456)
assert a == A(x=123, y=456)