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


767

Як я можу перетворити strпредставлення dictтакої, як наступна рядок, в a dict?

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

Я вважаю за краще не використовувати eval. Що ще я можу використовувати?

Основна причина цього - один із моїх колег, який він написав, перетворює всі матеріали в рядки. Я не в настрої ходити і модифікувати його заняття, займатися цим питанням.


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.
Martijn Pieters

Відповіді:


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 = розбір (node_or_string, mode = 'eval') Файл "D : \ Python26 \ lib \ ast.py ", рядок 36, у компіляції повернення для розбору (expr, ім'я файлу, режим, PyCF_ONLY_AST) Файл" <невідомий> ", рядок 1 ^ SyntaxError: недійсний синтаксис

як щодо "dict(a=1)"стильових рядків?
n611x007

Здається, це не працює для значення перерахунку всередині словника. Напр .: d = "{'col': <Colors.RED: 2>, 'val': 2}"
shivshnkr

3
чому б не використовувати json.dumps та json.loads insead, я знайшов це рішення більш елегантним татаном, використовуючи 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 видає помилку "Недійсна JSON" через уникнуту єдину цитату в JSON


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'>

13
Я не думаю, що це відповідає на відповідь ОП. Як ми використовуємо json.laads для перетворення рядка s = "{'muffin': 'lolz', 'foo': 'kitty'}" для диктування?
технозі

чому це друк 'u' у виході ?? напр. - str = '{"1": "P", "2": "N", "3": "M"}' d = json.loads (str) друк d вихідний: {u'1 ': u'P ', u'3': u'M ', u'2': u'N '}
користувач905

2
@technazi: json.loads (h.replace ("'",' "'))
ntg

Однак є обмеження, наприклад: h = '{"muffin": "lolz", "foo": "kitty",}', також h = '{"muffin's": "lolz", "foo": "kitty «}», (тільки помічена частина тих же коментарі в подібному відповіді ... залишаючи тут для повноти ...)
NTG

4
На мою думку, це найкоротший і найпростіший спосіб ... Однозначно той, який я особисто віддаю перевагу.
nostradamus

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

1
Це призведе до перетворення рядків "так" і "ні" в "True / False"
Ерік Маркос

23

Якщо рядку завжди можна довіряти, ви можете використовувати eval(або використовувати, literal_evalяк пропонується; це безпечно незалежно від того, що це за рядок.) Інакше вам потрібен аналізатор. Аналізатор JSON (наприклад, simplejson) працював би, якщо він коли-небудь зберігає вміст, який відповідає схемі JSON.


8
Починаючи з 2.6, simplejson включається в стандартну бібліотеку Python як модуль json.
Eli Courtwright

11
Так, це хороша відповідь, але зауважте, що офіційно JSON не підтримує одно цитовані рядки, як це наведено в прикладі оригінального афіші.
Бен Хойт

19

Використовуйте json. astбібліотека споживає багато пам'яті і і повільніше. У мене є процес, який повинен прочитати текстовий файл 156 Мб. 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("'")буде працювати лише для рядків, де дані "одиничні".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.