Python 冻结嵌套数据类,带有 __call__ = 替换

问题描述 投票:0回答:1

我正在尝试编写具有以下特征的不可变数据结构。

  1. 不变
  2. 轻松生成具有更改字段的不可变副本
  3. 可组合,因此更新嵌套数据就像更新非嵌套数据一样简单

我试图实现的API是这个

a0 = Person(name = 'Jhon', occupation = {'title': 'junear', 'sallary': 30})
a1 = a(name = a0.name + ' Smith')
a2 = a1(occupation = {'title': 'seanear'})
a3 = a2(occupation = {'sallary': 50})

我已经写了一个像这样的实现


from dataclasses import dataclass, replace, field

@dataclass(frozen=True)
class Occupation:
    __call__ = replace
    title: str
    sallary: int

@dataclass(frozen=True)
class Person:
    __call__ = replace
    name: str
    occupation: Occupation
    
    @property
    def occupation(self):
        return self._occupation

    @occupation.setter
    def occupation(self, value):
        if '_occupation' not in self.__dict__:
            print('initalising occupation')
            occ = Occupation
        else:
            print('updating occupation')
            occ = self.occupation

        if isinstance(value, tuple):
            object.__setattr__(self,'_occupation', occ(*value))
        elif isinstance(value, dict):
            object.__setattr__(self,'_occupation', occ(**value))
        elif isinstance(value, Occupation):
            object.__setattr(self,'_occupation', value)

    

但是,我在这里遇到了问题。

a0
工作正常,但其余的都失败了。 我相信问题在于复制/更新
_occupation
无人管理的字段。

问题:

  1. 有没有更简单的解决方案,我正在寻找
  2. 如何访问职业设置器中先前对象的数据?
  3. 如果有一种方法可以生成我编写的样板,当冻结数据类的参数之一是冻结数据类时生成,甚至内联子属性的类定义,那就太好了

谢谢你。

铌:

  1. 在编写这段代码时,我从这个线程中提取出来,并且我已经阅读了这个文档
python immutability python-dataclasses
1个回答
0
投票

为属性定义 setter 有点打破了你的不变性假设。您需要构造新的

Occupation
,然后使用它创建新的
Person


from dataclasses import dataclass, replace

@dataclass(frozen=True)
class Occupation:
    __call__ = replace
    title: str
    salary: int

@dataclass(frozen=True)
class Person:
    name: str
    occupation: Occupation


    def __call__(self, **kwargs):
        try:
            occupation = kwargs['occupation']
            if isinstance(occupation, tuple):
                occ = self.occupation(*occupation)
            elif isinstance(occupation, dict):
                occ = self.occupation(**occupation)
            elif isinstance(occupation, Occupation):
                occ = occupation
            kwargs['occupation'] = occ
        except KeyError:
            pass
        return replace(self, **kwargs)



a0 = Person(name = 'John', occupation = {'title': 'junior', 'salary': 30})
a1 = a0(name = a0.name + ' Smith')
a2 = a1(occupation = {'title': 'senior'})
a3 = a2(occupation = {'salary': 50})
© www.soinside.com 2019 - 2024. All rights reserved.