Python Dict转Object

227 阅读1分钟

背景

日常开发过程中经常碰到dict转object的场景,如何灵活优雅的转换?这里提供一种基于 dataclasses 装饰器的方式。

例子

from dataclasses import dataclass, asdict, is_dataclass

@dataclass  
class UpdateProgressRecord(object):  
    """ 更新进度记录 """    # 更新时间  
    update_time: str = ''  
    # 更新文件的md5  
    update_file_md5: str = ''  
    # 更新的 Id 列表  
    ids: list[int] = None  
    # 是否完成整个文件的整体更新  
    is_finished: bool = False  
    # 更新的开始行数  
    start_idx_include: int = 0  
    # 更新的结束行数  
    end_idx_exclude: int = 0  
    # 更新的向量数  
    updated_count: int = 0

value = {'ids':[1,2,3], 'update_count': 2}
# dict 转 object
update_record = UpdateProgressRecord(**value)

进阶

在实践中发现在序列化 UpdateProgressRecord 对象的时候出错,json.dumps 不支持 @dataclass 装饰的对象。通过自定义encoder解决,代码如下:

class EnhancedJSONEncoder(json.JSONEncoder):  
    """ dataclasses json encoder """  
  
    def default(self, o):  # pylint: disable=E0202  
        """ as dict """  
        if is_dataclass(o):  
            return asdict(o)  
        return super().default(o)

完整代码如下:

class EnhancedJSONEncoder(json.JSONEncoder):  
    """ dataclasses json encoder """  
  
    def default(self, o):  # pylint: disable=E0202  
        """ as dict """  
        if is_dataclass(o):  
            return asdict(o)  
        return super().default(o)

@dataclass  
class UpdateProgressRecord(object):  
    """ 更新进度记录 """    # 更新时间  
    update_time: str = ''  
    # 更新文件的md5  
    update_file_md5: str = ''  
    # 更新的 Id 列表  
    ids: list[int] = None  
    # 是否完成整个文件的整体更新  
    is_finished: bool = False  
    # 更新的开始行数  
    start_idx_include: int = 0  
    # 更新的结束行数  
    end_idx_exclude: int = 0  
    # 更新的向量数  
    updated_count: int = 0

value = {'ids':[1,2,3], 'update_count': 2}
# dict 转 object
update_record = UpdateProgressRecord(**value)
json.dumps(update_record, cls=EnhancedJSONEncoder, ensure_ascii=False)

干预dataclass更多进阶的内容可以学习:Data Classes in Python 3.7+ (Guide) – Real Python