Python OOP中__dict__赋值JSON引发AttributeError问题求助
嘿,这个问题我之前踩过坑!你猜得没错——问题就出在JSON加载出来的是纯字典/列表这类基本数据类型,直接把它们赋值给__dict__,你的Sentences实例里的属性确实会被填充,但那些嵌套的Words、Characters并不会自动变成类实例,还是保持着JSON加载后的dict形态,所以你用点语法访问charss的时候,自然会报错说dict没有这个属性。
绝对不要修改JSON输入!
先给你明确结论:修改JSON是最糟糕的方案。JSON的核心是作为通用数据格式存在的,要是为了适配你的类结构去改JSON,会让数据和代码强绑定——以后你改类的属性名,JSON也得跟着改;其他程序读取这个JSON也会一脸懵。完全得不偿失。
正确做法:实现自定义反序列化逻辑
你需要做的是把JSON加载后的dict,递归转换成对应的类实例。这里有几种方案,从手动实现到用库简化都有:
方案1:给每个类写from_dict类方法
这是最基础也最可控的方式,每个类自己负责把dict转换成实例,嵌套的类就递归调用:
import json class Characters: def __init__(self, char_content): self.charss = char_content # 这里根据你实际的属性调整,比如单个字符或者字符列表 @classmethod def from_dict(cls, data): # 把dict转换成Characters实例 return cls(char_content=data['charss']) class Words: def __init__(self, chars_list): self.charss = chars_list # 存储Characters实例的列表 @classmethod def from_dict(cls, data): # 递归处理每个字符dict,转成Characters实例 chars_instances = [Characters.from_dict(char_data) for char_data in data['charss']] return cls(chars_list=chars_instances) class Sentences: def __init__(self, words_list): self.w = words_list # 存储Words实例的列表 @classmethod def from_dict(cls, data): # 递归处理每个单词dict,转成Words实例 words_instances = [Words.from_dict(word_data) for word_data in data['w']] return cls(words_list=words_instances) # 加载并反序列化的正确姿势 data = json.loads(output.content) s = Sentences.from_dict(data) # 现在就能正常访问了! print(s.w[0].charss[0].charss)
方案2:用dataclasses简化(Python 3.7+)
如果你的类是纯数据类(没有复杂方法),用dataclasses可以减少重复代码,再配合手动的反序列化函数:
from dataclasses import dataclass from typing import List import json @dataclass class Characters: charss: str # 调整类型为你实际需要的,比如List[str] @dataclass class Words: charss: List[Characters] @dataclass class Sentences: w: List[Words] def deserialize_sentences(data: dict) -> Sentences: words = [] for word_data in data['w']: chars = [Characters(**char_data) for char_data in word_data['charss']] words.append(Words(charss=chars)) return Sentences(w=words) # 使用方式 data = json.loads(output.content) s = deserialize_sentences(data)
方案3:用pydantic一键搞定(最省心)
如果可以安装第三方库,pydantic是处理嵌套类序列化/反序列化的神器,它会自动帮你处理类型转换,几乎不用写额外代码:
from pydantic import BaseModel from typing import List import json class Characters(BaseModel): charss: str class Words(BaseModel): charss: List[Characters] class Sentences(BaseModel): w: List[Words] # 直接解析JSON字符串,自动转成嵌套实例 s = Sentences.parse_raw(output.content) # 正常访问属性 print(s.w[0].charss[0].charss)
为什么直接赋值__dict__不行?
__dict__是对象存储属性的字典,但它只是把键值对存进去,不会做任何类型校验或转换。JSON加载出来的w是dict列表,赋值给Sentences的w属性后,它还是dict列表,不是Words实例列表——所以你访问s.w[0]拿到的是dict,用.访问charss自然会报错,因为dict只能用[]访问键。
内容的提问来源于stack exchange,提问作者bcsta

