Comment puis-je convertir la représentation str
d'un dict
, telle que la chaîne suivante, en un dict
?
s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
Je préfère ne pas utiliser eval
. Que puis-je utiliser?
La principale raison en est que l’un de mes cours de collègues a été écrit, convertit toutes les entrées en chaînes. Je ne suis pas d'humeur à modifier ses cours pour régler ce problème.
À partir de Python 2.6, vous pouvez utiliser les fonctions intégrées ast.literal_eval
:
_>>> import ast
>>> ast.literal_eval("{'muffin' : 'lolz', 'foo' : 'kitty'}")
{'muffin': 'lolz', 'foo': 'kitty'}
_
C'est plus sûr que d'utiliser eval
. Comme le dit sa propre documentation:
>>> help (ast.literal_eval) Aide sur la fonction literal_eval dans le module ast: literal_eval (node_or_string) Évaluez en toute sécurité une expression noeud ou une chaîne contenant une expression Python . La chaîne ou le nœud fourni ne peut contenir que les structures littérales Python suivantes: chaînes, nombres, n-uplets, listes, dicts, booléens, Et aucun.
Par exemple:
_>>> 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
_
http://docs.python.org/2/library/json.html
JSON peut résoudre ce problème bien que son décodeur veuille des guillemets doubles autour des clés et des valeurs. Si cela ne vous dérange pas de remplacer le hack ...
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'}
REMARQUE: si vous utilisez des guillemets simples dans vos clés ou vos valeurs, cela échouera à cause d'un remplacement incorrect des caractères. Cette solution n'est recommandée que si vous avez une forte aversion pour la solution eval.
En savoir plus sur la citation simple json: citation simple jQuery dans une réponse JSON
en utilisant json.loads
:
>>> import json
>>> h = '{"foo":"bar", "foo2":"bar2"}'
>>> type(h)
<type 'str'>
>>> d = json.loads(h)
>>> d
{u'foo': u'bar', u'foo2': u'bar2'}
>>> type(d)
<type 'dict'>
Pour l'exemple d'OP:
s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
Nous pouvons utiliser Yaml pour traiter ce type de json non standard dans la chaîne:
>>> import yaml
>>> s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> s
"{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> yaml.load(s)
{'muffin': 'lolz', 'foo': 'kitty'}
Si la chaîne peut toujours être fiable, vous pouvez utiliser eval
(ou utiliser literal_eval
comme suggéré; c'est sûr, quelle que soit la chaîne.) Sinon, vous avez besoin d'un analyseur. Un analyseur JSON (tel que simplejson) fonctionnerait s’il ne stockait que le contenu compatible avec le schéma JSON.
Utilisez json
. la bibliothèque ast
consomme beaucoup de mémoire et plus lentement. J'ai un processus qui a besoin de lire un fichier texte de 156 Mo. Ast
avec 5 minutes de retard pour le dictionnaire de conversion json
et 1 minute avec 60% de mémoire en moins!
Résumer:
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('')
Résultats:
*** 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)
Conclusion: prefer json.loads
Vous pouvez essayer ça.
>>> import ast
>>> data = "{'user': 'bob', 'age': 10, 'grades': ['A', 'F', 'C']}"
>>> ast.literal_eval(data)
O/P: {'age': 10, 'grades': ['A', 'F', 'C'], 'user': 'bob'}
>>> user = ast.literal_eval(data)
>>> user['age']
O/P: 10
>>> user['grades']
O/P: ['A', 'F', 'C']
>>> user['user']
O/P: 'bob'
Si vous ne pouvez pas utiliser Python 2.6, vous pouvez utiliser une implémentation sécurisée simple telle que http://code.activestate.com/recipes/364469/
Il se greffe sur le compilateur Python afin que vous n'ayez pas à faire tout le travail grossier vous-même.
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
aucune bibliothèque n'est utilisée:
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))
NOTE: Comme il a codé en dur, split("'")
ne fonctionnera que pour les chaînes où les données sont "entre guillemets simples".