Сохранять кортежи Python с JSON
Я все еще немного новичок в этом, поэтому я мог бы не знать всех обычных терминов для вещей:
Можно ли сохранять кортежи Python при кодировании с помощью JSON? Прямо сейчас json.loads(json.dumps(tuple))
возвращает мне список. Я не хочу преобразовывать свои кортежи в списки, но я хочу использовать JSON. Итак, есть ли варианты?
Причина:
Я создаю приложение, которое использует многомерные массивы, не всегда одинаковой формы. У меня есть методы класса, которые используют рекурсию для проверки массивов и перевода конечных точек в виде строки или int. Недавно я понял, что (на основе того, как работает моя рекурсия) я могу использовать кортежи для предотвращения более глубокого рекурсивного поиска массивов (Python rawks). Это может пригодиться в ситуациях, когда я знаю, что я уверен, что мне не нужно будет углубляться в мои структуры данных.
Ответы
Ответ 1
Вы можете написать высокоспециализированный кодер и хук декодера:
import json
class MultiDimensionalArrayEncoder(json.JSONEncoder):
def encode(self, obj):
def hint_tuples(item):
if isinstance(item, tuple):
return {'__tuple__': True, 'items': item}
if isinstance(item, list):
return [hint_tuples(e) for e in item]
if isinstance(item, dict):
return {key: hint_tuples(value) for key, value in item.items()}
else:
return item
return super(MultiDimensionalArrayEncoder, self).encode(hint_tuples(obj))
def hinted_tuple_hook(obj):
if '__tuple__' in obj:
return tuple(obj['items'])
else:
return obj
enc = MultiDimensionalArrayEncoder()
jsonstring = enc.encode([1, 2, (3, 4), [5, 6, (7, 8)]])
print jsonstring
# [1, 2, {"items": [3, 4], "__tuple__": true}, [5, 6, {"items": [7, 8], "__tuple__": true}]]
print json.loads(jsonstring, object_hook=hinted_tuple_hook)
# [1, 2, (3, 4), [5, 6, (7, 8)]]
Ответ 2
Нет, это невозможно. В формате JSON нет понятия кортежа (см. здесь для краткого разбиения того, какие типы существуют в JSON). Модуль Python json
преобразует кортежи Python в списки JSON, потому что это самое близкое в JSON к кортежу.
Здесь вы не указали подробные сведения о своем прецеденте, но если вам нужно хранить строковые представления структур данных, содержащих кортежи, сразу приходят в голову несколько возможностей, которые могут быть или не быть подходящими в зависимости от вашей ситуации
- Создайте собственные функции кодирования и декодирования.
- Используйте pickle (осторожно;
pickle.loads
небезопасно использовать для ввода пользователем).
- Используйте
repr
и ast.literal_eval
вместо от json.dumps
и json.loads
. repr
даст вам результат, похожий по внешнему виду, на json.dumps
, но repr
не преобразует кортежи в списки. ast.literal_eval
- менее мощная, более безопасная версия eval
, которая будет расшифровывать только строки, числа, кортежи, списки, dicts, booleans, и None
.
Вариант 3 - это, пожалуй, самое простое и простое решение для вас.
Ответ 3
Это с simplejson
import simplejson
def _to_json(python_object) :
if isinstance(python_object, tuple) :
python_object = {'__class__': 'tuple',
'__value__': list(python_object)}
else :
raise TypeError(repr(python_object) + ' is not JSON serializable')
return python_object
def _from_json(json_object):
if json_object['__class__'] == 'tuple':
return tuple(json_object['__value__'])
return json_object
jsn = simplejson.dumps((1,2,3),
default=_to_json,
tuple_as_array=False)
tpl = simplejson.loads(jsn, object_hook=_from_json)
Ответ 4
Принципиальным отличием списков Python от кортежей является изменчивость, которая не имеет отношения к представлениям JSON, если вы не планируете изменять внутренние члены списка JSON, пока он находится в текстовой форме. Вы можете просто превратить списки, которые вы получаете обратно в кортежи. Если вы не используете какие-либо пользовательские декодеры объектов, единственными структурированными типами данных, которые вы должны учитывать, являются объекты и массивы JSON, которые выдаются как команды и списки Python.
def tuplify(listything):
if isinstance(listything, list): return tuple(map(tuplify, listything))
if isinstance(listything, dict): return {k:tuplify(v) for k,v in listything.items()}
return listything
Если вы специализируетесь на декодировании или хотите, чтобы некоторые массивы JSON были списками Python, а другие - кортежами Python, вам нужно будет обернуть элементы данных в dict или кортеж, который аннотирует информацию о типе. Само по себе это лучший способ влиять на поток управления алгоритма, чем ветвление, основанное на том, является ли что-то списком или кортежем (или другим итеративным типом).