Це досить складно, оскільки 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"}}