Bir sözlüğün dize ile temsilini sözlüğe dönüştürme?


768

Aşağıdaki dize gibi strbir a'nın temsilini a'ya nasıl dönüştürebilirim ?dictdict

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

Kullanmamayı tercih ederim eval. Başka ne kullanabilirim?

Bunun ana nedeni, yazdığı çalışma arkadaşlarım sınıflarından biri, tüm girdileri dizgilere dönüştürüyor. Bu konuyla ilgilenmek için sınıflarını değiştirme ve değiştirme havasında değilim.


1
Python 2.6 kullanamıyorsanız, aşağıdaki gibi basit bir safeeval implmenentation kullanabilirsiniz code.activestate.com/recipes/364469 Eğer brüt esere tüm kendiniz yapmak zorunda kalmamak Python derleyici üzerine bindirme yapar.
Ned Batchelder

11
Not : Buraya aldatıcı bir şekilde benzer görünen JSON verileri ile gelenler için, bunun yerine Python'daki Parse JSON'u okumak istersiniz . JSON, Python ile aynı şey değildir . Eğer varsa "sizin dizeleri çift tırnak muhtemelen JSON veri var. Ayrıca arayabilirsiniz null, trueveya false, Python'un sözdizimi kullanır None, Trueve False.
Martijn Pieters

Yanıtlar:


1167

Python 2.6'dan başlayarak, yerleşik olanı kullanabilirsiniz ast.literal_eval:

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

Bu kullanmaktan daha güvenlidir eval. Kendi belgelerinin dediği gibi:

>>> yardım (ast.literal_eval)
Ast modülünde literal_eval işlevi hakkında yardım:

literal_eval (node_or_string)
    Bir ifade düğümünü veya Python içeren bir dizeyi güvenle değerlendirin
    ifadesi. Sağlanan dize veya düğüm yalnızca aşağıdakilerden oluşabilir
    Python değişmez yapıları: stringler, sayılar, tuples, listeler, dikte, boolean,
    ve Yok.

Örneğin:

>>> 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 ile kullanmak için dizeyi sterilize etmeniz gerektiğini eklemeliyim. (tırnak işaretleri / dize çift tırnak kaçının)
Paulo Matos

Bu hatayı alıyorum Windows 7 x64 dosya üzerinde python 2.6 (x86) üzerinde "D: \ Python26 \ lib \ ast.py", satır 48, literal_eval node_or_string = ayrıştırma (node_or_string, mode = 'eval') Dosya "D : \ Python26 \ lib \ ast.py ", satır 36, ayrıştırma dönüş derlemesinde (ifade, dosya adı, mod, PyCF_ONLY_AST)" <unknown> "dosyası, satır 1 ^ Sözdizimi

"dict(a=1)"stil dizeleri ne olacak ?
n611x007

Bu bir sözlük içindeki numaralandırma değeri için işe yaramaz gibi görünüyor. Örnek: d = "{'col': <Renk.RED: 2>, 'val': 2}"
shivshnkr

3
neden json.dumps ve json.loads insead kullanmıyorum, bu çözümü eval kullanarak daha uygun buldum
Auros132

232

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

Kod çözücü, anahtarlar ve değerler etrafında çift tırnak istese de JSON bu sorunu çözebilir. Değişim hack sakıncası yoksa ...

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

Anahtarlarınızın veya değerlerinizin bir parçası olarak tek tırnak işareti varsa, bunun yanlış karakter değişimi nedeniyle başarısız olacağını unutmayın. Bu çözüm yalnızca eval çözümüne karşı güçlü bir isteksizlik varsa önerilir.

Json tek alıntı hakkında daha fazla bilgi: jQuery.parseJSON, JSON'daki tek tırnak işareti nedeniyle "Geçersiz JSON" hatası veriyor


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

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

1
Bu çözümü arıyordum. +1kod çözücünün anahtarlar ve değerler etrafında çift tırnak istediğini bildirmek için.
h8pathak

Başka bir sorun ise "{0: 'Hello'}".
Finn Årup Nielsen

3
Eğer, (değil JSON uyumlu) virgül sondaki varsa bu da başarısız örneğin: "{ 'çörek': 'lolz', 'foo': 'pisi',}"
guival

159

kullanarak 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
OP'nin cevabına cevap verdiğini sanmıyorum. Dikkate almak için s = "{'muffin': 'lolz', 'foo': 'kitty'}" dizesini dönüştürmek için json.laads'ı nasıl kullanabiliriz?
technazi

neden bu baskı çıktı 'u'? ör. - str = '{"1": "P", "2": "N", "3": "M"}' d = json.loads (str) baskı d çıktısı: {u'1 ': u'P ', u'3': u'M ', u'2': u'N '}
kullanıcı905

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

Ancak, sınırlar vardır, örneğin: h = '{"muffin": "lolz", "foo": "pisi",}', ayrıca h = '{"muffin": "lolz", "foo": "pisi "} ', (sadece aynı yorumların bir kısmını benzer bir cevapta fark ettim ... hala tamlık için buradan ayrılıyor ...)
ntg

4
Bence, bu en kısa ve en kolay yol ... Kesinlikle kişisel olarak tercih ettiğim yöntem.
nostradamus

35

OP örneğine:

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

Dize içinde bu tür standart olmayan json ile başa çıkmak için Yaml'ı kullanabiliriz :

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

1
Bu, 'evet' ve 'hayır' dizelerinin Doğru / Yanlış'a dönüştürülmesine neden olur
Eric Marcos

23

Dize her zaman güvenilebiliyorsa, kullanabilirsiniz eval(veya literal_evalönerildiği gibi kullanabilirsiniz ; dize ne olursa olsun güvenlidir.) Aksi takdirde bir ayrıştırıcıya ihtiyacınız vardır. Bir JSON ayrıştırıcısı (simplejson gibi), yalnızca JSON şemasına uyan içeriği depolaması halinde çalışır.


8
2.6'dan başlayarak, simplejson, Python standart kütüphanesine json modülü olarak dahil edilmiştir.
Eli Courtwright

11
Evet, bu iyi bir cevap, ancak resmi olarak JSON'un orijinal poster örneğinde verildiği gibi tek tırnaklı dizeleri desteklemediğini unutmayın.
Ben Hoyt

19

Kullanın json. astKütüphane çok fazla bellek ve ve daha yavaş tüketir. 156Mb bir metin dosyasını okumak için gereken bir işlemim var. Astdönüşüm sözlüğü için 5 dakika gecikme jsonve% 60 daha az bellek kullanarak 1 dakika!


13
ancak sınırları vardır: "{'foo': 'bar',}" dizesini dönüştürmeyi deneyin
ntg

12

Özetlemek:

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

Sonuçlar:

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

Sonuç: json.loads'u tercih edin


5
Bunun dışında, ilk sorunun bir parçası olan tek tırnaklı dize ile çalışmaz. Performanstan hiç bahsedilmedi.
Michael Campbell

1
Vay be .... Süper Açıklama ....
smack kiraz

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
Bu yaklaşımda birçok hata var. Bir anahtarın değeri {veya içeriyorsa ne olur }. Ya iç içe geçmişse dict. Değer içeriyorsa ne olur ,?
Om Sao

4

hiç libs kullanılmaz:

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

NOT: Sabit kodlanmış split("'")olduğu için yalnızca verinin "tek tırnaklı" olduğu dizelerde çalışacaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.