这个问题比较棘手,因为namedtuple()
是一个工厂函数,它返回一个从tuple
派生的新类型。一种方法是让你的类也继承UserDict.DictMixin
,但是tuple.__getitem__
已经定义并且期望一个整数来表示元素的位置,而不是属性的名称:
>>> f = foobar('a', 1)
>>> f[0]
'a'
从本质上讲,命名元组(namedtuple)与JSON不太匹配,因为它实际上是一个定制类型,其键名在类型定义中被固定,而字典的键名则存储在实例内部。这使得你无法对一个命名元组进行"往返"操作。例如,你不能将一个字典解码回一个命名元组,除非字典中包含其他信息,比如应用特定的类型标记{'a': 1, '#_type': 'foobar'}
,这有点麻烦。
虽然这种情况并不理想,但如果你只需要将命名元组编码为字典,另一种方法是扩展或修改你的JSON编码器以针对这些类型进行特殊处理。以下是一个示例,演示了如何子类化Python的json.JSONEncoder
来确保嵌套的命名元组正确地转换为字典:
from collections import namedtuple
from json import JSONEncoder
class MyEncoder(JSONEncoder):
def _iterencode(self, obj, markers=None):
if isinstance(obj, tuple) and hasattr(obj, '_asdict'):
gen = self._iterencode_dict(obj._asdict(), markers)
else:
gen = JSONEncoder._iterencode(self, obj, markers)
for chunk in gen:
yield chunk
class foobar(namedtuple('f', 'foo, bar')):
pass
enc = MyEncoder()
for obj in (foobar('a', 1), ('a', 1), {'outer': foobar('x', 'y')}):
print enc.encode(obj)
{"foo": "a", "bar": 1}
["a", 1]
{"outer": {"foo": "x", "bar": "y"}}