Convertire una rappresentazione in formato stringa di un dizionario in un dizionario?


768

Come posso convertire la strrappresentazione di a dict, come la seguente stringa, in a dict?

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

Preferisco non usare eval. Cos'altro posso usare?

Il motivo principale di ciò, è una delle mie lezioni di colleghi che ha scritto, converte tutti gli input in stringhe. Non sono in vena di andare a modificare le sue lezioni, per affrontare questo problema.


1
Se non è possibile utilizzare Python 2.6, è possibile utilizzare una semplice implmenentazione sicura come code.activestate.com/recipes/364469 Sulle spalle del compilatore Python, quindi non è necessario eseguire tutto il lavoro da soli.
Ned Batchelder,

11
Nota : per quelli che arrivano qui con dati JSON apparentemente simili , si consiglia di leggere Parse JSON in Python . JSON non è la stessa cosa di Python . Se hai delle doppie virgolette intorno alle tue stringhe, probabilmente hai dei dati JSON. È anche possibile cercare , o , usi Python sintassi , e . "nulltruefalseNoneTrueFalse
Martijn Pieters

Risposte:


1167

A partire da Python 2.6 puoi usare il built-in ast.literal_eval:

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

Questo è più sicuro dell'uso eval. Come dicono i suoi documenti:

>>> aiuto (ast.literal_eval)
Aiuto sulla funzione literal_eval nel modulo ast:

literal_eval (node_or_string)
    Valuta in modo sicuro un nodo di espressione o una stringa contenente un Python
    espressione. La stringa o il nodo forniti possono consistere solo in quanto segue
    Strutture letterali di Python: stringhe, numeri, tuple, liste, dadi, booleani,
    e nessuno.

Per esempio:

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

Dovrei aggiungere che devi disinfettare la stringa per usarla con ast.literal_eval. (assicurati che le virgolette / doppie virgolette nella stringa siano evase)
Paulo Matos

ottengo questo errore Sono su Python 2.6 (x86) su Windows 7 x64 File "D: \ Python26 \ lib \ ast.py", linea 48, in letterale_evale node_or_string = parse (node_or_string, mode = 'eval') File "D : \ Python26 \ lib \ ast.py ", riga 36, ​​nella compilazione di ritorno dell'analisi (expr, nome file, modalità, PyCF_ONLY_AST) File" <unknown> ", riga 1 ^ Sintassi

che dire delle "dict(a=1)"stringhe di stile?
n611x007,

Questo non sembra funzionare per il valore enum all'interno di un dizionario. Ad esempio: d = "{'col': <Colors.RED: 2>, 'val': 2}"
shivshnkr

3
perché non usare json.dumps e json.loads insead, ho trovato questa soluzione più elevata di quanto non abbia usato eval
Auros132

232

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

JSON può risolvere questo problema anche se il suo decodificatore vuole virgolette doppie attorno a chiavi e valori. Se non ti dispiace un trucco sostitutivo ...

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

NOTA: se hai virgolette singole come parte delle tue chiavi o dei tuoi valori, ciò fallirà a causa di una sostituzione errata del carattere. Questa soluzione è consigliata solo se hai una forte avversione per la soluzione eval.

Ulteriori informazioni sulla virgoletta singola json: jQuery.parseJSON genera l'errore "JSON non valido" a causa della virgoletta singola con escape in JSON


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

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

1
Stavo cercando questa soluzione. +1per aver informato che il decodificatore desidera virgolette doppie attorno a chiavi e valori.
h8pathak,

Un altro problema è per "{0: 'Hello'}".
Finn Årup Nielsen,

3
Ciò fallisce anche se hai virgole finali (non conforme a JSON), ad esempio: "{'muffin': 'lolz', 'foo': 'kitty',}"
guival

159

utilizzando 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
Non credo che risponda alla risposta del PO. Come possiamo usare json.laads per convertire una stringa s = "{'muffin': 'lolz', 'foo': 'kitty'}" in dict?
technazi,

perché questa stampa 'u' nell'output ?? es. - str = '{"1": "P", "2": "N", "3": "M"}' d = json.loads (str) stampa d l'output è: {u'1 ': u'P ', u'3': u'M ', u'2': u'N '}
user905

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

Tuttavia, ci sono limiti, ad esempio: h = '{"muffin": "lolz", "foo": "kitty",}', anche h = '{"muffin's": "lolz", "foo": "kitty "} ', (
ho

4
Secondo me, questo è il modo più breve e semplice ... Sicuramente quello che preferisco personalmente.
nostradamus,

35

Nell'esempio di OP:

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

Possiamo usare Yaml per gestire questo tipo di json non standard nella stringa:

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

1
Ciò causerà la conversione delle stringhe "sì" e "no" in Vero / Falso
Eric Marcos,

23

Se la stringa può sempre essere attendibile, è possibile utilizzare eval(o utilizzare literal_evalcome suggerito; è sicuro indipendentemente dalla stringa.) Altrimenti è necessario un parser. Un parser JSON (come simplejson) funzionerebbe se memorizza solo contenuti che si adattano allo schema JSON.


8
A partire dalla 2.6, simplejson è incluso nella libreria standard di Python come modulo json.
Eli Courtwright,

11
Sì, questa è una buona risposta, ma nota che ufficialmente JSON non supporta stringhe a virgoletta singola, come indicato nell'esempio del poster originale.
Ben Hoyt,

19

Usa json. la astlibreria consuma molta memoria e, più lentamente. Ho un processo che deve leggere un file di testo di 156 Mb. Astcon 5 minuti di ritardo per il dizionario di conversione jsone 1 minuto con il 60% di memoria in meno!


13
ma ha i suoi limiti: prova a convertire la stringa "{'foo': 'bar',}"
ntg

12

Riassumere:

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

risultati:

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

Conclusione: preferire json.loads


5
Tranne questo non funzionerà con la sua stringa a virgoletta singola, che faceva parte del suo problema iniziale. Le prestazioni non sono mai state menzionate.
Michael Campbell,

1
Wow .... Super Explanation ....
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
Molti errori in questo approccio. Cosa succede se il valore di una chiave contiene {o }. E se fosse nidificato dict. Cosa succede se il valore contiene ,??
Om Sao

4

non vengono utilizzate lib lib:

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

NOTA: dato che ha hardcoded split("'")funzionerà solo per stringhe in cui i dati sono "a virgoletta singola".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.