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"}}