Python 具有可变和不可变属性的数据类样式对象?

Python 具有可变和不可变属性的数据类样式对象?,python,immutability,python-3.7,mutable,python-dataclasses,Python,Immutability,Python 3.7,Mutable,Python Dataclasses,我一直在玩从文件中动态加载属性名的数据类,我找不到一种方法来创建“冻结”和“非冻结”属性。我相信数据类只允许您将所有属性设置为冻结或非冻结 到目前为止,我创建了一个冻结的数据类,并添加了一个可变类作为属性之一,我可以随时更改这些属性,但我对这种方法的可读性不是很满意 人们会推荐另外一个pythonic数据类,而不需要实现一个能够设置可变/不可变属性的类吗 导入数据类 类ModifiableConfig: “这里有东西,但你明白了。” ... config_dataclass=dataclasse

我一直在玩从文件中动态加载属性名的数据类,我找不到一种方法来创建“冻结”和“非冻结”属性。我相信数据类只允许您将所有属性设置为冻结或非冻结

到目前为止,我创建了一个冻结的数据类,并添加了一个可变类作为属性之一,我可以随时更改这些属性,但我对这种方法的可读性不是很满意

人们会推荐另外一个pythonic数据类,而不需要实现一个能够设置可变/不可变属性的类吗

导入数据类
类ModifiableConfig:
“这里有东西,但你明白了。”
...
config_dataclass=dataclasses.make_dataclass(
"c",,
[(x,键入(x),v)表示x,在config.items()中键入v++[('var',object,ModifiableConfig())],
冻结=真
)
但是,我更希望能够选择哪些属性是冻结的,哪些不是冻结的。使向dataclass添加额外类的需要过时。可能是这样的:

config_dataclass_modifiable = dataclasses.make_dataclass(
            'c', [(x, type(x), v, True if 'modifiable' in x else False) for x, v in config.items()])

注意x else False中的“True if‘modifiable’”,我并不是说我最终会这样做,但希望这有助于更好地理解我的问题。

调整属性处理的正常方法是编写一个自定义方法,允许您覆盖属性分配的默认行为。不幸的是,dataclasses也采用了这种方法来强制执行
冻结
逻辑,该逻辑通过抛出
TypeError:Cannot overwrite class ModifiableConfig
中的属性uu_setattr_u_u_u_u>来有效地阻止函数被进一步修改

因此,对于你的问题,我看不到一个简单直接的解决方案。在我看来,您将类的可变部分委托给内部对象或字典的方法并不坏,或者根本不是pythonic,但是如果您愿意从需求列表中删除
freezed
,并且只想要一个部分可变的数据类,您可以尝试在此处使用此盗版半冻结配方,该配方使用标记
semi
更新
dataclass
装饰器,您可以打开该标记以获得所描述的行为:

从数据类将数据类导入为dc
从回溯导入格式\u堆栈
def数据类(_cls=None,*,init=True,repr=True,eq=True,order=False,
不安全(散列=假、冻结=假、半=假):
def包裹(cls):
#检查新kw是否正常
如果是半成品:
如果冻结:
提高AttributeError(“半冻结或冻结,而不是两者兼而有之。”)
如果cls.\uuuuu setattr\uuuuuu!=cls.mro()
raise ATTRIBEERROR(“使用semi时禁止触摸setattr!”)
#运行原始数据类装饰器
dc(cls,init=init,repr=repr,eq=eq,order=order,
不安全散列=不安全散列,冻结=冻结)
#添加半冻结逻辑
如果是半成品:
定义设置属性(自身、键、值):
如果输入self.\uuuu插槽\uuuu:
caller=format_stack()[-2].rsplit('in',1)[1].strip()
如果来电者__初始值:
raise TypeError(f“属性“{key}”是不可变的!”)
对象。设置属性(self、key、value)
cls.\uuuuu setattr\uuuuuuuu=\uuuuuuuuuu setattr__
返回cls
#使用或不使用参数调用句柄
如果_cls为无:
回程包装
返回包装(cls)
我在这里只是简单介绍一下,这里不讨论一些潜在的边缘案例。有更好的方法来处理包装,以便内部更加一致,但这会使这个已经很复杂的代码片段变得更加复杂

给定这个新的
dataclass
decorator,您可以像这样使用它来定义具有一些不可变属性和一些可变属性的dataclass:

>@dataclass(semi=True)
... Foo类:
...     # 将不可变属性和dict放入插槽
...     __槽位=(“指令”、“x”、“y”)
...     x:int
...     y:int
...     z:int
...
>>>f=Foo(1,2,3)
>>>f#打印Foo(x=1,y=2,z=3)
>>>f.z=4#将起作用
>>>f.x=4#引发类型错误:属性“x”是不可变的!

您不必使用
\uuuuu slots\uuuuu
来区分可变部分和不可变部分,但它很方便,因为有几个原因(例如作为一个元属性,它不是默认数据类的一部分
repr
),我感觉很直观。

在上面的顶部答案中,如果
Foo
是另一个类的子类,则代码将中断。要解决此问题,请执行以下命令:

super(type(self), self).__setattr__(key, value)
应改为:

super(type(cls), cls).__setattr__(key, value)

这样,super实际上是向上遍历的,而不是无限的自引用。

IMO如果您添加了一个示例,说明您所拥有的内容(可能有一些示例用法),而您认为这些内容不太可读,这将非常有帮助。这会让我们更好地了解你在说什么。谢谢你的回复!!我要试一试!我希望它有用=)如果某些东西没有按预期工作,请随时返回。它不是那么简单,您的解决方案在基本情况下出现了
TypeError:无法将此u setattr\uu应用于type object
。更新类上的属性而不是实例上的属性也很奇怪,在没有检查属性的情况下,我假设所有实例上的更改都是可见的。我将看看是否可以修复setattr中的mro,使其与继承一起工作,但可能是不可能有一个干净的解决方案-将继承与类装饰器混合在一起一开始是非常可疑的。顺便说一句,感谢您找到了错误,我更新了我的答案以使用
对象,除非某个父类定义了本应使用的自定义
\uuuu setattr\uuuu
。是的,您是对的。解决办法是多方面的