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.

UberJumper
sumber
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
Jacob Gabrielson
sumber
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

0x539
sumber
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'>
tokhi
sumber
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'}
lqhcpsgbl
sumber
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.

Blixt
sumber
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!

Rogerio Silveira
sumber
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

Anatoly Alekseev
sumber
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
Siva Kameswara Rao Munipalle
sumber
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".

tamerlaha
sumber