如何将Python复杂嵌套类转换为字典?有哪些可用工具?
问题描述
我有若干自定义类,其实例包含str、int、float、list、dict等基础类型,同时还嵌套了其他同类型的自定义类,形成多层嵌套对象。这些数据以对象形式方便后续操作,处理完成后我希望将其转换为嵌套字典,从而存储为数据库中的JSON对象。这种需求常见吗?有没有可实现该功能的库?
目前我打算自己实现一个函数来完成转换,但担心递归实现会导致栈溢出。以下是我的代码雏形:
def getObjectAsDict(self): d = {} for name_of_attr in dir(some_class): if name_of_attr.startswith("_"): continue value_of_attr = getattr(some_class, name_of_attr) if isinstance(value_of_attr, str): pass elif isinstance(value_of_attr, int): pass elif isinstance(value_of_attr, float): pass elif isinstance(value_of_attr, bool): pass elif isinstance(value_of_attr, list): for idx, item in enumerate(value_of_attr): # some recursion logic here elif isinstance(value_of_attr, dict): for key, value in value_of_attr.items(): # some recursion logic here elif isinstance(value_of_attr, ComplexObject): value_of_attr = value_of_attr.getObjectAsDict() # some recursion logic here else: continue d[name_of_attr] = value_of_attr
回答
需求常见性:这种需求非常常见,ORM对象转数据库JSON字段、API接口序列化数据、复杂配置对象持久化等场景里,都经常需要把嵌套自定义对象转成可序列化的字典结构。
可用成熟库:
dataclasses.asdict:如果你的类是用Python3.7+的dataclasses装饰器定义的,直接调用dataclasses.asdict()就能自动处理嵌套的dataclass对象,递归逻辑做了常规场景的优化,不用担心普通深度的栈溢出。pydantic:如果需要序列化+数据校验的组合能力,Pydantic模型自带的dict()方法可以完美处理嵌套模型,还支持自定义序列化规则,底层实现对递归深度做了合理限制。attrs:和dataclasses类似,attrs库的asdict()方法也能轻松转换嵌套的attrs类实例为字典,用法简洁。
手动实现优化建议:如果坚持自己写逻辑,你的代码雏形可以做这些优化:
- 别用
dir(some_class)遍历属性,dir()会返回大量类内置属性和方法,建议直接指定需要序列化的属性列表,或者过滤出实例属性且非方法的项。 - 基础类型不需要单独写
pass,直接加入字典即可。 - 把递归逻辑抽成独立辅助函数,避免重复代码。
- 针对极深嵌套场景,改用迭代方式(用栈/队列存储待处理对象),彻底规避栈溢出风险。
- 别用
优化后的递归版示例:
def obj_to_dict(obj): # 直接返回基础类型 if isinstance(obj, (str, int, float, bool, type(None))): return obj # 处理列表:递归转换每个元素 elif isinstance(obj, list): return [obj_to_dict(item) for item in obj] # 处理字典:递归转换每个值 elif isinstance(obj, dict): return {key: obj_to_dict(value) for key, value in obj.items()} # 处理自定义对象:过滤非私有、非方法属性后递归转换 else: result = {} for attr_name in dir(obj): if attr_name.startswith("_"): continue attr_value = getattr(obj, attr_name) if callable(attr_value): continue result[attr_name] = obj_to_dict(attr_value) return result # 示例自定义类 class ComplexObject: def __init__(self): self.name = "demo" self.count = 456 self.child = ComplexObject() # 嵌套测试 obj = ComplexObject() print(obj_to_dict(obj))
迭代版(无栈溢出风险)示例:
def obj_to_dict_iterative(obj): stack = [(obj, None, {})] result = {} while stack: current_obj, parent_key, parent_container = stack.pop() # 处理基础类型 if isinstance(current_obj, (str, int, float, bool, type(None))): if parent_key is not None: if isinstance(parent_container, list): parent_container[parent_key] = current_obj else: parent_container[parent_key] = current_obj else: result = current_obj continue # 处理列表 elif isinstance(current_obj, list): new_list = [] if parent_key is not None: parent_container[parent_key] = new_list else: result = new_list # 倒序入栈保证处理顺序正确 for idx, item in enumerate(reversed(current_obj)): stack.append((item, idx, new_list)) # 处理字典 elif isinstance(current_obj, dict): new_dict = {} if parent_key is not None: parent_container[parent_key] = new_dict else: result = new_dict for key, value in reversed(current_obj.items()): stack.append((value, key, new_dict)) # 处理自定义对象 else: new_dict = {} if parent_key is not None: parent_container[parent_key] = new_dict else: result = new_dict # 过滤非私有、非方法属性后倒序入栈 attrs = [(attr, getattr(current_obj, attr)) for attr in dir(obj) if not attr.startswith("_") and not callable(getattr(current_obj, attr))] for attr_name, attr_value in reversed(attrs): stack.append((attr_value, attr_name, new_dict)) return result
内容的提问来源于stack exchange,提问作者tyleax
相关产品推荐
相关产品推荐

