Jest to dość trudne, ponieważ namedtuple()jest to fabryka, która zwraca nowy typ pochodzący z tuple. Jedną z metod byłoby ustawienie klasy dziedziczącej także po UserDict.DictMixin, ale tuple.__getitem__jest już zdefiniowana i oczekuje liczby całkowitej oznaczającej pozycję elementu, a nie nazwy jego atrybutu:
>>> f = foobar('a', 1)
>>> f[0]
'a'
W istocie namedtuple nie pasuje do JSON, ponieważ jest to w rzeczywistości niestandardowy typ, którego nazwy kluczy są ustalone jako część definicji typu , w przeciwieństwie do słownika, w którym nazwy kluczy są przechowywane wewnątrz instancji. Zapobiega to „przełączaniu w obie strony” nazwanego tulei, np. Nie można dekodować słownika z powrotem do nazwanego tulei bez innej informacji, takiej jak znacznik typu specyficznego dla aplikacji w dyktcie {'a': 1, '#_type': 'foobar'}, co jest nieco zepsute.
Nie jest to idealne rozwiązanie, ale jeśli potrzebujesz tylko zakodować namedtuples w słownikach, innym podejściem jest rozszerzenie lub zmodyfikowanie kodera JSON do specjalnych przypadków tych typów. Oto przykład podklasy Pythona json.JSONEncoder. To rozwiązuje problem zapewnienia, że zagnieżdżone krotki nazw są prawidłowo konwertowane na słowniki:
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"}}