T1、魔术工具
json 封装
class JSONWrapper:
def __init__(self, data):
# 这种属性设置会调用__setattr__, 导致实例字典__dict__中不设置__data属性
# 进而实例属性找不到,会调用__getattr__, 其中又有self.__data又找不到,又会调用__getattr__,无限循环
# self.__data = data
# 使用__dict__直接访问实例的属性,避免调用__setattr__
self.__dict__['_JSONWrapper__data'] = data
def __getattr__(self, name):
# 支持属性访问字典键
if isinstance(self.__data, dict):
try:
return self._wrap(self.__data[name])
except KeyError:
raise AttributeError(f"Attribute '{name}' not found")
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
def __setattr__(self, name, value):
# 如果是字典类型,将新的属性设置到字典中
if isinstance(self.__data, dict):
self.__data[name] = value
else:
raise TypeError("Cannot set attribute '{}' on non-dict type.".format(name))
def __getitem__(self, key):
# 支持字典和列表的索引访问
if isinstance(self.__data, (dict, list)):
try:
return self._wrap(self.__data[key])
except (KeyError, IndexError) as e:
raise AttributeError(f"Key or Index '{key}' not found") from e
raise TypeError(f"Unsupported operation for type '{type(self.__data).__name__}'")
def __setitem__(self, key, value):
# 如果是字典类型,设置对应的键值
if isinstance(self.__data, dict):
self.__data[key] = value
# 如果是列表类型,更新索引位置的值
elif isinstance(self.__data, list):
self.__data[key] = value
else:
raise TypeError("Unsupported operation for type '{}'".format(type(self.__data).__name__))
def __iter__(self):
# 迭代内部的列表或字典对象
if isinstance(self.__data, (list, dict)):
for item in self.__data:
yield self._wrap(item)
else:
raise TypeError(f"'{type(self.__data).__name__}' object is not iterable")
def __repr__(self):
# 返回一个对象的详细表示,适合在开发和调试中使用
return '{}({})'.format(self.__class__.__name__, repr(self.__data))
__str__ = __repr__
@staticmethod
def _wrap(value):
# 包装嵌套的字典和列表
if isinstance(value, (dict, list)):
return JSONWrapper(value)
return value
def __bool__(self):
# 判断封装的对象是否为空
if not self.__data:
return False
return True
# 兼容 Python 2.7
__nonzero__ = __bool__
@property
def raw(self):
# 拿数据结构,非对象
return self.__data
# 示例用法
data = {
"status": "normal",
"storage_pool": [
{
"logical_pools": [
"default_ec_data",
"default_ec_data_replication"
],
"name": "storage_pool_1",
"id": 1
}
],
"extern": {
"pool_partition": {
"eds_fe997806": {
"phxtier": [
{
"data": {
"size": 79,
"sn": "eds-fe997806-0",
"device_no": "2"
}
},
{
"data": {
"size": 80,
"sn": "eds-fe997806-1",
"device_no": "3"
}
}
]
}
}
}
}
wrapped_data = JSONWrapper(data)
# 示例访问
print(wrapped_data.status) # 输出: normal
print(wrapped_data.storage_pool[0].name) # 输出: storage_pool_1
print(wrapped_data.extern.pool_partition["eds_fe997806"].phxtier[0].data.size) # 输出: 79
wsgi 中的 json.dump 为什么不能序列化类对象,有 circle reference 问题?
在使用 wsgi 中的 json.dumps 或类似的 JSON 序列化方法时,无法直接序列化自定义类的对象,这通常是因为 Python 的默认 JSONEncoder 不知道如何处理复杂的对象。
json.dumps 默认只能处理以下数据类型:
- 基本类型:如 str, int, float, bool, None
- Python 内置的容器类型:如 list, tuple, dict
- 自定义的类对象(如 JSONWrapper、数据库模型对象等)不属于这些类型,因此 json.dumps 无法直接序列化这些对象。尝试序列化时会引发 TypeError: Object of type X is not JSON serializable 异常。
- 而 circle reference(循环引用)的问题通常出现在自定义类中,如果对象的属性或嵌套结构导致相互引用或对象的递归结构,JSON 序列化会认为它是一个循环引用,无法处理。
如何处理无法序列化的问题?
要让 json.dumps 正确处理自定义类对象,需要提供一个自定义的序列化器或将对象转换为 Python 内置的数据结构(如 dict)。