python - 在 asdict 或序列化的数据类中包含属性的推荐方法是什么?

标签 python python-3.x serialization immutability python-dataclasses

请注意,这类似于 How to get @property methods in asdict? .
我有一个(卡住的)嵌套数据结构,如下所示。定义了一些(纯粹)依赖于字段的属性。

import copy
import dataclasses
import json
from dataclasses import dataclass

@dataclass(frozen=True)
class Bar:
    x: int
    y: int

    @property
    def z(self):
        return self.x + self.y

@dataclass(frozen=True)
class Foo:
    a: int
    b: Bar

    @property
    def c(self):
        return self.a + self.b.x - self.b.y
我可以将数据结构序列化如下:
class CustomEncoder(json.JSONEncoder):
    def default(self, o):
        if dataclasses and dataclasses.is_dataclass(o):
            return dataclasses.asdict(o)
        return json.JSONEncoder.default(self, o)

foo = Foo(1, Bar(2,3))
print(json.dumps(foo, cls=CustomEncoder))

# Outputs {"a": 1, "b": {"x": 2, "y": 3}}
但是,我还想序列化属性( @property )。注意我不想使用 __post_init__ 将属性转换为字段。因为我想保持数据类的卡住。 I do not want to use obj.__setattr__ to work around the frozen fields.我也不想预先计算类外属性的值并将它们作为字段传递。
我正在使用的当前解决方案是明确写出每个对象的序列化方式,如下所示:
class CustomEncoder2(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, Foo):
            return {
                "a": o.a,
                "b": o.b,
                "c": o.c
            }
        elif isinstance(o, Bar):
            return {
                "x": o.x,
                "y": o.y,
                "z": o.z
            }
        return json.JSONEncoder.default(self, o)

foo = Foo(1, Bar(2,3))
print(json.dumps(foo, cls=CustomEncoder2))

# Outputs {"a": 1, "b": {"x": 2, "y": 3, "z": 5}, "c": 0} as desired
对于几级嵌套,这是可以管理的,但我希望有一个更通用的解决方案。例如,这是一个(hacky)解决方案,它对数据类库中的 _asdict_inner 实现进行猴子补丁。
def custom_asdict_inner(obj, dict_factory):
    if dataclasses._is_dataclass_instance(obj):
        result = []
        for f in dataclasses.fields(obj):
            value = custom_asdict_inner(getattr(obj, f.name), dict_factory)
            result.append((f.name, value))
        # Inject this one-line change
        result += [(prop, custom_asdict_inner(getattr(obj, prop), dict_factory)) for prop in dir(obj) if not prop.startswith('__')]
        return dict_factory(result)
    elif isinstance(obj, tuple) and hasattr(obj, '_fields'):
        return type(obj)(*[custom_asdict_inner(v, dict_factory) for v in obj])
    elif isinstance(obj, (list, tuple)):
        return type(obj)(custom_asdict_inner(v, dict_factory) for v in obj)
    elif isinstance(obj, dict):
        return type(obj)((custom_asdict_inner(k, dict_factory),
                          custom_asdict_inner(v, dict_factory))
                         for k, v in obj.items())
    else:
        return copy.deepcopy(obj)

dataclasses._asdict_inner = custom_asdict_inner

class CustomEncoder3(json.JSONEncoder):
    def default(self, o):
        if dataclasses and dataclasses.is_dataclass(o):
            return dataclasses.asdict(o)
        return json.JSONEncoder.default(self, o)

foo = Foo(1, Bar(2,3))
print(json.dumps(foo, cls=CustomEncoder3))

# Outputs {"a": 1, "b": {"x": 2, "y": 3, "z": 5}, "c": 0} as desired
有没有推荐的方法来实现我想要做的事情?

最佳答案

这似乎与方便的 dataclass 相矛盾特征:

Class(**asdict(obj)) == obj  # only for classes w/o nested dataclass attrs
如果你没有找到任何相关的 pypi 包,你总是可以像这样添加一个 2-liner:
from dataclasses import asdict as std_asdict

def asdict(obj):
    return {**std_asdict(obj),
            **{a: getattr(obj, a) for a in getattr(obj, '__add_to_dict__', [])}}
然后,您可以在 dicts 中以自定义但简短的方式指定您想要的内容:
@dataclass
class A:
    f: str
    __add_to_dict__ = ['f2']

    @property
    def f2(self):
        return self.f + '2'



@dataclass
class B:
    f: str

print(asdict(A('f')))
print(asdict(B('f')))
:
{'f2': 'f2', 'f': 'f'}
{'f': 'f'}

关于python - 在 asdict 或序列化的数据类中包含属性的推荐方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64777931/

相关文章:

python-3.x - 使用 native TWS Python APi(Interactive Brokers API),如何在变量中获取证券列表的价格快照?

python - 2-opt 算法解决 Python 中的旅行商问题

java - 如何在 Java 客户端-服务器模型中设计消息

c# - protobuf-net [反] 跨程序集边界序列化

R:将对象序列化到文本文件并再次返回

python - 在 Python 中使用 atexit 注册实例方法有什么意义?

python - 如果使用 Python 字符串列表中存在某些元素,则获取列表索引的有效方法

python - 无法使用 Anaconda-Python 导入 scitools

python - 如何动态地将属性添加到类中?

python-3.x - 合并 pandas "data frame.style"对象并输出到 html