具有可变和不可变属性的数据类样式的对象? [英] Dataclass-style object with mutable and immutable properties?

查看:106
本文介绍了具有可变和不可变属性的数据类样式的对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在使用从文件中动态加载属性名称的数据类进行操作,但是我无法找到创建冻结"和非冻结"属性的方法.我相信数据类仅允许您将所有属性设置为冻结或非冻结.

I have been playing around with dataclasses dynamically loaded with property names from a file and I am unable to find a way to create both 'frozen' and 'non-frozen' properties. I believe dataclasses only allow you to set all properites to frozen or non-frozen.

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

As of now, I create a frozen dataclass and add a mutable class as one of the properties which I can change as I go but I am not very happy with the readability of this approach.

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

Is there another pythonic dataclass people would recommend without needing to implement a class with the ability to set mutable/immutable properties?

import dataclasses

class ModifiableConfig:
    """There is stuff in here but you get the picture."""
    ...

config_dataclass = dataclasses.make_dataclass(
    'c',
    [(x, type(x), v) for x, v in config.items()] + [('var', object, ModifiableConfig())],
    frozen=True
)

但是,我希望能够选择冻结哪些属性,哪些不冻结.不再需要向数据类添加其他类.可能看起来像这样:

However I would prefer the ability to choose which attributes are frozen and which are not. Making the need of adding an additional class to the dataclass obsolete. It may look like this:

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中为'modifiable',则为True",我并不是说这是我最终会做的事情,但希望这有助于更好地理解我的问题.

Notice the "True if 'modifiable' in x else False", I'm not saying this is how I would do it in the end but hopefully this helps understand my question better.

推荐答案

调整属性处理的正常方法是编写自定义

The normal approach to tuning attribute handling is writing a custom __setattr__ method which allows you to override the default behavior for attribute assignments. Unfortunately, that method is also what dataclasses hooks into to enforce the frozen logic, which effectively locks the function from being altered any further by throwing TypeError: Cannot overwrite attribute __setattr__ in class ModifiableConfig as soon as you try to touch it.

因此,我看不到您的问题的简单明了的解决方案.在我看来,将类的可变部分委派给内部对象或字典的方法完全不错,也没有Python的含义,但是如果您可以从需求列表中删除frozen且只需要一个部分可变的数据类,您可以尝试在此处使用该bootleg半冷冻配方,使用标志semi更新dataclass装饰器,可以打开该标志以获取您描述的行为:

As a consequence, there is no straight forward and simple solution to your problem that I can see. Your approach of delegating the mutable parts of a class to an inner object or dictionary is, in my opinion, not bad or un-pythonic at all, but if you're fine with dropping frozen from your requirements list and only want a partly-mutable dataclass, you can try using this bootleg-semi-frozen recipe here that updates the dataclass decorator with a flag semi that you can switch on to get the behavior you described:

from dataclasses import dataclass as dc
from traceback import format_stack

def dataclass(_cls=None, *, init=True, repr=True, eq=True, order=False,
              unsafe_hash=False, frozen=False, semi=False):

    def wrap(cls):
        # sanity checks for new kw
        if semi:
            if frozen:
                raise AttributeError("Either semi or frozen, not both.")
            if cls.__setattr__ != cls.mro()[1].__setattr__:
                raise AttributeError("No touching setattr when using semi!")

        # run original dataclass decorator
        dc(cls, init=init, repr=repr, eq=eq, order=order,
           unsafe_hash=unsafe_hash, frozen=frozen)

        # add semi-frozen logic
        if semi:
            def __setattr__(self, key, value):
                if key in self.__slots__:
                    caller = format_stack()[-2].rsplit('in ', 1)[1].strip()
                    if caller != '__init__':
                        raise TypeError(f"Attribute '{key}' is immutable!")
                super(type(self), self).__setattr__(key, value)
            cls.__setattr__ = __setattr__

        return cls

    # Handle being called with or without parens
    if _cls is None:
        return wrap
    return wrap(_cls)

我在这里很简短,只处理看来对我来说可能是缺点的案例.有更好的方法来处理包装,以使内部结构更加一致,但这会使本来就很复杂的代码片段炸得更厉害.

I'm being brief here and only handle the cases that look like probable shortcomings to me. There are better ways to handle the wrapping so that the internals are more consistent, but it would blow this already complicated snippet up even more.

使用这个新的dataclass装饰器,您可以像这样使用它来定义一个具有一些不可变属性和一些可变属性的数据类:

Given this new dataclass decorator, you can use it like this to define a dataclass with some immutable attributes and some mutable ones:

>>> @dataclass(semi=True)
... class Foo:
...     # put immutable attributes and __dict__ into slots 
...     __slots__ = ('__dict__', 'x', 'y')
...     x: int
...     y: int
...     z: int
...
>>> f = Foo(1, 2, 3)
>>> f        # prints Foo(x=1, y=2, z=3)
>>> f.z = 4  # will work
>>> f.x = 4  # raises TypeError: attribute 'x' is immutable!

您不必使用__slots__来将可变的部分与不可变的部分分开,但是出于某些原因(例如,不是默认数据类repr的元属性),它很方便. ),对我来说很直观.

You don't have to use __slots__ to separate the mutable from the immutable part, but it is convenient for a few reasons (such as being a meta-attribute that isn't part of the default dataclass repr) and felt intuitive to me.

这篇关于具有可变和不可变属性的数据类样式的对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆