¿Convertir una representación de cadena de un diccionario en un diccionario?

768

¿Cómo puedo convertir la strrepresentación de a dict, como la siguiente cadena, en a dict?

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

Yo prefiero no utilización eval. ¿Qué más puedo usar?

La razón principal de esto, es una de mis clases de compañeros de trabajo que escribió, convierte todas las entradas en cadenas. No estoy de humor para ir y modificar sus clases, para lidiar con este problema.

UberJumper
fuente
1
Si no puede usar Python 2.6, puede usar una implicación segura simple como code.activestate.com/recipes/364469 Se complementa con el compilador de Python para que no tenga que hacer todo el trabajo bruto usted mismo.
Ned Batchelder
11
Nota : Para aquellos que vienen aquí con datos JSON engañosamente similares , en su lugar, desea leer Parse JSON en Python . JSON no es lo mismo que Python . Si tiene comillas dobles alrededor de sus cadenas, probablemente tenga datos JSON. También puede buscar , o , usos de sintaxis de Python , y . "nulltruefalseNoneTrueFalse
Martijn Pieters

Respuestas:

1167

A partir de Python 2.6, puede usar el incorporado ast.literal_eval:

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

Esto es más seguro que usarlo eval. Como dicen sus propios documentos:

>>> ayuda (ast.literal_eval)
Ayuda sobre la función literal_eval en el módulo ast:

literal_eval (nodo_o_cadena)
    Evalúe de forma segura un nodo de expresión o una cadena que contenga un Python
    expresión. La cadena o nodo proporcionado solo puede consistir en lo siguiente
    Estructuras literales de Python: cadenas, números, tuplas, listas, dictos, booleanos,
    y ninguno

Por ejemplo:

>>> 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
fuente
Debo agregar que necesita desinfectar la cadena para usar con ast.literal_eval. (asegúrese de que las comillas / comillas dobles se escapen)
Paulo Matos
me sale este error Estoy en python 2.6 (x86) en Windows 7 x64 Archivo "D: \ Python26 \ lib \ ast.py", línea 48, en literal_eval node_or_string = parse (node_or_string, mode = 'eval') Archivo "D : \ Python26 \ lib \ ast.py ", línea 36, ​​en compilación de retorno de análisis (expr, nombre de archivo, modo, PyCF_ONLY_AST) Archivo" <unknown> ", línea 1 ^ SyntaxError: sintaxis no válida
¿Qué pasa con las "dict(a=1)"cadenas de estilo?
n611x007
Esto no parece funcionar para el valor de enumeración dentro de un diccionario. Por ejemplo: d = "{'col': <Colors.RED: 2>, 'val': 2}"
shivshnkr
3
por qué no usar json.dumps y json.loads insead, encontré esta solución más elevada que usando eval
Auros132
232

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

JSON puede resolver este problema, aunque su decodificador quiere comillas dobles alrededor de claves y valores. Si no te importa reemplazar un truco ...

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: si tiene comillas simples como parte de sus claves o valores, esto fallará debido a un reemplazo incorrecto de caracteres. Esta solución solo se recomienda si tiene una fuerte aversión a la solución eval.

Más acerca de la comilla simple de json: jQuery.parseJSON arroja el error "JSON no válido" debido a una comilla simple escapada en JSON

0x539
fuente
12
{"foo": "b'ar"}
Mark E. Haase
44
{'foo': (1, 2, 3)}
Mark E. Haase
1
Estaba buscando esta solución. +1para informar que el decodificador quiere comillas dobles alrededor de claves y valores.
h8pathak
Otro problema es para "{0: 'Hello'}".
Finn Årup Nielsen
3
Esto también falla si tiene comas finales (no compatibles con JSON), por ejemplo: "{'muffin': 'lolz', 'foo': 'kitty',}"
guival
159

utilizando 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
fuente
13
No creo que responda la respuesta del OP. ¿Cómo usamos json.laads para convertir una cadena s = "{'muffin': 'lolz', 'foo': 'kitty'}" para dictar?
technazi
¿Por qué esta impresión 'u' en la salida? por ejemplo, str = '{"1": "P", "2": "N", "3": "M"}' d = json.loads (str) print d la salida es: {u'1 ': u'P ', u'3': u'M ', u'2': u'N '}
usuario905
2
@technazi: json.loads (h.replace ("'",' "'))
ntg
Sin embargo, hay límites, por ejemplo: h = '{"muffin": "lolz", "foo": "kitty",}', también h = '{"muffin's": "lolz", "foo": "kitty "} ', (acabo de notar parte de los mismos comentarios en una respuesta similar ... todavía
salgo de
44
En mi opinión, esa es la forma más corta y fácil ... Definitivamente la que yo personalmente prefiero.
nostradamus
35

Para el ejemplo de OP:

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

Podemos usar Yaml para tratar este tipo de json no estándar en cadena:

>>> import yaml
>>> s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> s
"{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> yaml.load(s)
{'muffin': 'lolz', 'foo': 'kitty'}
lqhcpsgbl
fuente
1
Esto hará que las cadenas 'sí' y 'no' se conviertan en Verdadero / Falso
Eric Marcos
23

Si siempre se puede confiar en la cadena , puede usar eval(o usar literal_evalcomo se sugiere; es seguro sin importar cuál sea la cadena). De lo contrario, necesita un analizador. Un analizador JSON (como simplejson) funcionaría si solo almacena contenido que se ajuste al esquema JSON.

Blixt
fuente
8
A partir de 2.6, simplejson se incluye en la biblioteca estándar de Python como módulo json.
Eli Courtwright
11
Sí, esa es una buena respuesta, pero tenga en cuenta que oficialmente JSON no admite cadenas entre comillas simples, como se muestra en el ejemplo del póster original.
Ben Hoyt
19

Uso json. la astbiblioteca consume mucha memoria y más lento. Tengo un proceso que necesita leer un archivo de texto de 156Mb. Ast¡con 5 minutos de retraso para el diccionario de conversión jsony 1 minuto con un 60% menos de memoria!

Rogerio Silveira
fuente
13
pero tiene sus límites: intente convertir la cadena "{'foo': 'bar',}"
ntg
12

Para resumir:

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

Resultados:

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

Conclusión: prefiera json.loads

Anatoly Alekseev
fuente
55
Excepto que esto no funcionará con su cadena entre comillas simples, que fue parte de su problema inicial. El rendimiento nunca fue mencionado.
Michael Campbell
1
Wow .... Super Explicación ....
golpea cereza
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
fuente
3
Muchos errores en este enfoque. ¿Qué pasa si el valor de una clave contiene {o }. ¿Qué pasa si está anidado dict? ¿Qué pasa si el valor contiene ,?
Om Sao
4

no se utilizan libs:

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: Como se ha codificado split("'"), funcionará solo para cadenas en las que los datos están "entre comillas simples".

tamerlaha
fuente