Konversi representasi String dari Kamus ke kamus?


768

Bagaimana saya bisa mengubah strrepresentasi a dict, seperti string berikut, menjadi a dict?

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

Saya lebih suka tidak menggunakan eval. Apa lagi yang bisa saya gunakan?

Alasan utama untuk ini, adalah salah satu kelas rekan kerja saya yang ia tulis, mengubah semua input menjadi string. Saya tidak berminat untuk pergi dan memodifikasi kelasnya, untuk menangani masalah ini.


1
Jika Anda tidak dapat menggunakan Python 2.6, Anda dapat menggunakan impleeentasi safeeval sederhana seperti code.activestate.com/recipes/364469 Ini mendukung pada kompiler Python sehingga Anda tidak harus melakukan semua pekerjaan kotor sendiri.
Ned Batchelder

11
Catatan : Bagi mereka yang datang ke sini dengan data JSON yang tampak mirip , Anda ingin membaca Parse JSON dengan Python . JSON tidak sama dengan Python . Jika Anda memiliki tanda kutip ganda di sekitar string Anda, Anda mungkin memiliki data JSON. Anda juga bisa mencari , atau menggunakan sintaksis Python , dan . "nulltruefalseNoneTrueFalse
Martijn Pieters

Jawaban:


1167

Mulai dengan Python 2.6 Anda bisa menggunakan built-in ast.literal_eval:

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

Ini lebih aman daripada menggunakan eval. Seperti dokumen sendiri katakan:

>>> bantuan (ast.literal_eval)
Bantuan pada fungsi literal_eval di modul ast:

literal_eval (node_or_string)
    Mengevaluasi simpul ekspresi atau string yang mengandung Python dengan aman
    ekspresi. String atau simpul yang disediakan mungkin hanya terdiri dari yang berikut ini
    Struktur literal Python: string, angka, tupel, daftar, dicts, boolean,
    dan Tidak ada.

Sebagai contoh:

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

Saya harus menambahkan bahwa Anda perlu membersihkan string untuk digunakan dengan ast.literal_eval. (pastikan tanda kutip / kutip ganda dalam string diloloskan)
Paulo Matos

saya mendapatkan kesalahan ini Saya menggunakan python 2.6 (x86) di windows 7 x64 File "D: \ Python26 \ lib \ ast.py", baris 48, dalam literal_eval node_or_string = parse (node_or_string, mode = 'eval') File "D : \ Python26 \ lib \ ast.py ", baris 36, dalam kompilasi parse return (expr, nama file, mode, PyCF_ONLY_AST) File" <unknown> ", baris 1 ^ SintaksError: sintaks tidak valid

bagaimana dengan "dict(a=1)"gaya string?
n611x007

Ini tampaknya tidak berfungsi untuk nilai enum di dalam kamus. Misalnya: d = "{'col': <Colours.RED: 2>, 'val': 2}"
shivshnkr

3
mengapa tidak menggunakan json.dumps dan json.loads insead, saya menemukan solusi ini lebih tinggi daripada menggunakan eval
Auros132

232

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

JSON dapat memecahkan masalah ini meskipun dekoder ingin tanda kutip ganda di sekitar kunci dan nilai. Jika Anda tidak keberatan peretasan ganti ...

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

Perhatikan bahwa jika Anda memiliki tanda kutip tunggal sebagai bagian dari kunci atau nilai Anda, ini akan gagal karena penggantian karakter yang tidak benar. Solusi ini hanya disarankan jika Anda memiliki keengganan yang kuat terhadap solusi eval.

Lebih lanjut tentang json single quote: jQuery.parseJSON melempar kesalahan "JSON Tidak Sah" karena lolos dari penawaran tunggal di JSON


12
{"foo": "b'ar"}
Mark E. Haase

4
{'foo': (1, 2, 3)}
Mark E. Haase

1
Saya mencari solusi ini. +1untuk menginformasikan bahwa decoder menginginkan tanda kutip ganda di sekitar kunci dan nilai.
h8pathak

Masalah lain adalah untuk "{0: 'Hello'}".
Finn Årup Nielsen

3
Ini juga gagal jika Anda memiliki tanda koma (tidak sesuai JSON), misalnya: "{'muffin': 'lolz', 'foo': 'kitty',}"
guival

159

menggunakan 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
Saya tidak berpikir itu menjawab jawaban OP. Bagaimana kita menggunakan json.laads untuk mengonversi string s = "{'muffin': 'lolz', 'foo': 'kitty'}" menjadi dict?
technazi

mengapa ini mencetak 'u' di output ?? mis. - str = '{"1": "P", "2": "N", "3": "M"}' d = json.loads (str) print d output adalah: {u'1 ': u'P ', u'3': u'M ', u'2': u'N '}
user905

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

Namun, ada batasan, misalnya: h = '{"muffin": "lolz", "foo": "kitty",}', juga h = '{"muffin": "lolz", "foo": "kitty "} ', (perhatikan sebagian dari komentar yang sama dalam jawaban yang sama ... masih pergi ke sini untuk kelengkapan ...)
ntg

4
Menurut pendapat saya, itu cara terpendek dan termudah ... Jelas yang saya sukai secara pribadi.
nostradamus

35

Untuk contoh OP:

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

Kita dapat menggunakan Yaml untuk menangani json non-standar semacam ini dalam string:

>>> import yaml
>>> s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> s
"{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> yaml.load(s)
{'muffin': 'lolz', 'foo': 'kitty'}

1
Ini akan menyebabkan string 'ya' dan 'tidak' dikonversi menjadi Benar / Salah
Eric Marcos

23

Jika string selalu dapat dipercaya, Anda dapat menggunakan eval(atau menggunakan literal_evalseperti yang disarankan; aman apa pun string itu.) Jika tidak, Anda memerlukan parser. Pengurai JSON (seperti simplejson) akan berfungsi jika ia hanya pernah menyimpan konten yang sesuai dengan skema JSON.


8
Mulai dari 2.6, simplejson termasuk dalam pustaka standar Python sebagai modul json.
Eli Courtwright

11
Ya, itu jawaban yang bagus, tetapi perhatikan bahwa JSON secara resmi tidak mendukung string dengan kutip tunggal, seperti yang diberikan dalam contoh poster asli.
Ben Hoyt

19

Gunakan json. yang astperpustakaan mengkonsumsi banyak memori dan dan lebih lambat. Saya memiliki proses yang perlu membaca file teks 156Mb. Astdengan 5 menit keterlambatan untuk kamus konversi jsondan 1 menit menggunakan 60% lebih sedikit memori!


13
tetapi ada batasnya: coba konversi string "{'foo': 'bar',}"
ntg

12

Untuk meringkas:

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

Hasil:

*** 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)

Kesimpulan: lebih suka json.loads


5
Kecuali ini tidak akan bekerja dengan string yang dikutip satu kali, yang merupakan bagian dari masalah awalnya. Kinerja tidak pernah disebutkan.
Michael Campbell

1
Wow .... Penjelasan Super ....
smack cherry

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
Banyak kesalahan dalam pendekatan ini. Bagaimana jika nilai kunci berisi {atau }. Bagaimana jika itu bersarang dict. Bagaimana jika nilainya mengandung ,??
Om Sao

4

tidak ada lib yang digunakan:

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

CATATAN: Karena hardcoded split("'")hanya akan berfungsi untuk string di mana data "dikutip tunggal".

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.