Преобразовать строковое представление словаря в словарь?

769

Как я могу преобразовать strпредставление a dict, такое как следующая строка, в a dict?

s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"

Я предпочитаю не использовать eval. Что еще я могу использовать?

Основная причина этого, один из написанных им классов моих коллег, преобразует все входные данные в строки. Я не в настроении идти и модифицировать свои занятия, чтобы заниматься этим вопросом.

UberJumper
источник
1
Если вы не можете использовать Python 2.6, вы можете использовать простую безопасную имплементацию, такую ​​как code.activestate.com/recipes/364469. Это позволяет использовать компилятор Python, поэтому вам не нужно выполнять всю грубую работу самостоятельно.
Нед Бэтчелдер
11
Примечание . Для тех, кто приходит сюда с обманчиво похожими данными JSON , вы должны вместо этого прочитать Parse JSON в Python . JSON - это не то же самое, что Python . Если у вас есть "двойные кавычки вокруг строк, у вас, вероятно, есть данные JSON. Вы также можете искать null, trueили false, синтаксис Python использует None, Trueи False.
Мартин Питерс

Ответы:

1167

Начиная с Python 2.6 вы можете использовать встроенный ast.literal_eval:

>>> import ast
>>> ast.literal_eval("{'muffin' : 'lolz', 'foo' : 'kitty'}")
{'muffin': 'lolz', 'foo': 'kitty'}

Это безопаснее, чем использовать eval. Как говорят его собственные документы:

>>> помощь (ast.literal_eval)
Справка по функции literal_eval в модуле ast:

literal_eval (node_or_string)
    Безопасно оценивать узел выражения или строку, содержащую Python
    выражение. Предоставленная строка или узел может состоять только из следующих
    Литеральные структуры Python: строки, числа, кортежи, списки, диктанты, логические значения,
    и нет.

Например:

>>> eval("shutil.rmtree('mongo')")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
  File "/opt/Python-2.6.1/lib/python2.6/shutil.py", line 208, in rmtree
    onerror(os.listdir, path, sys.exc_info())
  File "/opt/Python-2.6.1/lib/python2.6/shutil.py", line 206, in rmtree
    names = os.listdir(path)
OSError: [Errno 2] No such file or directory: 'mongo'
>>> ast.literal_eval("shutil.rmtree('mongo')")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/Python-2.6.1/lib/python2.6/ast.py", line 68, in literal_eval
    return _convert(node_or_string)
  File "/opt/Python-2.6.1/lib/python2.6/ast.py", line 67, in _convert
    raise ValueError('malformed string')
ValueError: malformed string
Джейкоб Габриэльсон
источник
Я должен добавить, что вам нужно очистить строку для использования с ast.literal_eval. (убедитесь, что экранированы кавычки / двойные кавычки в строке)
Пауло Матос
я получаю эту ошибку Я на Python 2.6 (x86) в Windows 7 x64 Файл "D: \ Python26 \ lib \ ast.py", строка 48, в literal_eval node_or_string = parse (node_or_string, mode = 'eval') Файл "D : \ Python26 \ lib \ ast.py ", строка 36, в результате разбора возвращает компиляцию (expr, имя файла, режим, PyCF_ONLY_AST). Файл" <unknown> ", строка 1 ^ SyntaxError: неверный синтаксис
А как насчет "dict(a=1)"стилей?
n611x007
Кажется, это не работает для enum-значения внутри словаря. Например: d = "{'col': <Colors.RED: 2>, 'val': 2}"
shivshnkr
3
почему не используйте json.dumps и json.loads, я нашел это решение более подходящим, чем eval
Auros132
232

https://docs.python.org/3.8/library/json.html

JSON может решить эту проблему, хотя его декодеру нужны двойные кавычки вокруг ключей и значений. Если вы не возражаете взломать замену ...

import json
s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
json_acceptable_string = s.replace("'", "\"")
d = json.loads(json_acceptable_string)
# d = {u'muffin': u'lolz', u'foo': u'kitty'}

Обратите внимание, что если у вас есть одинарные кавычки как часть ваших ключей или значений, это не удастся из-за неправильной замены символов. Это решение рекомендуется только в случае сильного отвращения к решению eval.

Подробнее о одиночной кавычке json: jQuery.parseJSON выдает ошибку «Invalid JSON» из-за экранированной одинарной кавычки в JSON

0x539
источник
12
{"foo": "b'ar"}
Марк Э. Хаазе
4
{'foo': (1, 2, 3)}
Марк Э. Хаас
1
Я искал это решение. +1для информирования, что декодер хочет двойные кавычки вокруг ключей и значений.
h8pathak
Еще одна проблема для "{0: 'Hello'}".
Финн Оруп Нильсен
3
Это также не работает, если у вас есть запятые (не совместимые с JSON), например: "{'muffin': 'lolz', 'foo': 'kitty',}"
guival
159

используя json.loads:

>>> import json
>>> h = '{"foo":"bar", "foo2":"bar2"}'
>>> d = json.loads(h)
>>> d
{u'foo': u'bar', u'foo2': u'bar2'}
>>> type(d)
<type 'dict'>
tokhi
источник
13
Я не думаю, что это отвечает на ответ ОП. Как мы используем json.laads для преобразования строки s = "{'muffin': 'lolz', 'foo': 'kitty'}" для диктовки?
технази
почему эта печать 'U' в выводе? например - str = '{"1": "P", "2": "N", "3": "M"}' d = json.loads (str) print d выводит: {u'1 ': u'P ', u'3': u'M ', u'2': u'N '}
user905
2
@technazi: json.loads (h.replace ("", ""))
нтг
Однако существуют ограничения, например: h = '{"muffin": "lolz", "foo": "kitty",}', также h = '{"muffin's": "lolz", "foo": "kitty "} ', (только что заметил часть тех же комментариев в аналогичном ответе ... все еще оставляю здесь для полноты ...)
ntg
4
На мой взгляд, это самый короткий и простой способ ... Определенно тот, который я лично предпочитаю.
Нострадамус
35

К примеру ОП:

s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"

Мы можем использовать Yaml для обработки такого нестандартного json в строке:

>>> import yaml
>>> s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> s
"{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> yaml.load(s)
{'muffin': 'lolz', 'foo': 'kitty'}
lqhcpsgbl
источник
1
Это приведет к тому, что строки 'yes' и 'no' будут преобразованы в True / False
Эрик Маркос,
23

Если строке всегда можно доверять, вы можете использовать eval(или использовать literal_evalкак предложено; это безопасно, независимо от того, какая строка.) В противном случае вам нужен парсер. Парсер JSON (например, simplejson) будет работать, если он только хранит контент, который соответствует схеме JSON.

Blixt
источник
8
Начиная с версии 2.6, simplejson включен в стандартную библиотеку Python как модуль json.
Эли Кортрайт
11
Да, это хороший ответ, но обратите внимание, что официально JSON не поддерживает строки в одинарных кавычках, как показано в примере исходного плаката.
Бен Хойт
19

Использование json. astбиблиотека потребляет много памяти и и медленнее. У меня есть процесс, который должен прочитать текстовый файл 156Mb. Astс 5 минутной задержкой для словаря преобразования jsonи 1 минутой, используя на 60% меньше памяти!

Рожерио Сильвейра
источник
13
но имеет свои ограничения: попробуйте преобразовать строку "{'foo': 'bar',}"
ntg
12

Обобщить:

import ast, yaml, json, timeit

descs=['short string','long string']
strings=['{"809001":2,"848545":2,"565828":1}','{"2979":1,"30581":1,"7296":1,"127256":1,"18803":2,"41619":1,"41312":1,"16837":1,"7253":1,"70075":1,"3453":1,"4126":1,"23599":1,"11465":3,"19172":1,"4019":1,"4775":1,"64225":1,"3235":2,"15593":1,"7528":1,"176840":1,"40022":1,"152854":1,"9878":1,"16156":1,"6512":1,"4138":1,"11090":1,"12259":1,"4934":1,"65581":1,"9747":2,"18290":1,"107981":1,"459762":1,"23177":1,"23246":1,"3591":1,"3671":1,"5767":1,"3930":1,"89507":2,"19293":1,"92797":1,"32444":2,"70089":1,"46549":1,"30988":1,"4613":1,"14042":1,"26298":1,"222972":1,"2982":1,"3932":1,"11134":1,"3084":1,"6516":1,"486617":1,"14475":2,"2127":1,"51359":1,"2662":1,"4121":1,"53848":2,"552967":1,"204081":1,"5675":2,"32433":1,"92448":1}']
funcs=[json.loads,eval,ast.literal_eval,yaml.load]

for  desc,string in zip(descs,strings):
    print('***',desc,'***')
    print('')
    for  func in funcs:
        print(func.__module__+' '+func.__name__+':')
        %timeit func(string)        
    print('')

Результаты:

*** short string ***

json loads:
4.47 µs ± 33.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
builtins eval:
24.1 µs ± 163 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
ast literal_eval:
30.4 µs ± 299 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
yaml load:
504 µs ± 1.29 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

*** long string ***

json loads:
29.6 µs ± 230 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
builtins eval:
219 µs ± 3.92 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
ast literal_eval:
331 µs ± 1.89 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
yaml load:
9.02 ms ± 92.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Вывод: предпочитаю json.loads

Анатолий Алексеев
источник
5
За исключением того, что это не будет работать с его строкой в ​​одинарных кавычках, которая была частью его первоначальной проблемы. Производительность никогда не упоминалась.
Майкл Кэмпбелл
1
Ничего себе .... Супер объяснение ....
чмок вишня
5
string = "{'server1':'value','server2':'value'}"

#Now removing { and }
s = string.replace("{" ,"")
finalstring = s.replace("}" , "")

#Splitting the string based on , we get key value pairs
list = finalstring.split(",")

dictionary ={}
for i in list:
    #Get Key Value pairs separately to store in dictionary
    keyvalue = i.split(":")

    #Replacing the single quotes in the leading.
    m= keyvalue[0].strip('\'')
    m = m.replace("\"", "")
    dictionary[m] = keyvalue[1].strip('"\'')

print dictionary
Шива Камешвара Рао Мунипалле
источник
3
Много ошибок в этом подходе. Что если значение ключа содержит {или }. Что делать, если оно вложено dict. Что если значение содержит ,??
Ом Сан
4

никакие библиотеки не используются:

dict_format_string = "{'1':'one', '2' : 'two'}"
d = {}
elems  = filter(str.isalnum,dict_format_string.split("'"))
values = elems[1::2]
keys   = elems[0::2]
d.update(zip(keys,values))

ПРИМЕЧАНИЕ. Поскольку он жестко закодирован, он split("'")будет работать только для строк, в которых данные имеют одинарные кавычки.

tamerlaha
источник