Это довольно сложно, поскольку namedtuple()
- это фабрика, которая возвращает новый тип, полученный из tuple
. Один из подходов состоит в том, чтобы ваш класс также наследовал от UserDict.DictMixin
, но tuple.__getitem__
уже определен и ожидает целое число, обозначающее положение элемента, а не имя его атрибута:
>>> f = foobar('a', 1)
>>> f[0]
'a'
В своей основе namedtuple является нечетным соответствием для JSON, поскольку это действительно пользовательский тип, имена ключей которого фиксированы как часть определения типа , в отличие от словаря, в котором хранятся имена ключей внутри экземпляра. Это предотвращает «круговое отключение» именованного кортежа, например, Вы не можете декодировать словарь обратно в именованный кортеж без какой-либо другой части информации, например, маркера типа для конкретного приложения в dict {'a': 1, '#_type': 'foobar'}
, что немного хакерски.
Это не идеально, но , если вам нужно только кодировать namedtuples в словари, другой подход заключается в расширении или изменении вашего 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"}}