4

我想知道使以下数据类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
4

3 回答 3

3

dataclass没有对此的内置支持。Frozen-ness 是在类范围的基础上跟踪的,而不是每个实例,并且不支持自动生成数据类的冻结或解冻等效项。

虽然您可以尝试做一些事情来动态生成新的数据类,但它与 , 和其他您想要工作的东西的交互非常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)
于 2019-05-10T00:58:36.453 回答
3

Python 数据类很棒,但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
于 2019-05-10T01:27:48.560 回答
1

改变数据类的标准方法frozen是使用dataclasses.replace

old_bar = Bar(foo=123)
new_bar = dataclasses.replace(old_bar, foo=456)
assert new_bar.foo == 456

对于更复杂的用例,您可以使用来自以下网址的 dataclass utils 模块: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()))

如示例中所示,您可以返回数据类的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)
于 2022-01-26T14:06:00.337 回答