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.