Eine String-Darstellung eines Wörterbuchs in ein Wörterbuch konvertieren?

768

Wie kann ich die strDarstellung von a dict, z. B. der folgenden Zeichenfolge, in a konvertieren dict?

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

Ich bevorzuge es nicht zu benutzen eval. Was kann ich noch verwenden?

Der Hauptgrund dafür ist, dass eine meiner Kollegenklassen, die er geschrieben hat, alle Eingaben in Zeichenfolgen umwandelt. Ich bin nicht in der Stimmung, seine Klassen zu modifizieren, um dieses Problem zu lösen.

UberJumper
quelle
1
Wenn Sie Python 2.6 nicht verwenden können, können Sie eine einfache sichere Implementierung wie code.activestate.com/recipes/364469 verwenden. Sie huckepack auf dem Python-Compiler, sodass Sie nicht die ganze grobe Arbeit selbst erledigen müssen.
Ned Batchelder
11
Hinweis : Für diejenigen, die mit täuschend ähnlich aussehenden JSON- Daten hierher kommen , möchten Sie stattdessen Parse JSON in Python lesen . JSON ist nicht dasselbe wie Python . Wenn "Ihre Zeichenfolgen doppelte Anführungszeichen enthalten, verfügen Sie wahrscheinlich über JSON-Daten. Sie können auch einen Blick null, trueoder false, Python - Syntax verwendet None, Trueund False.
Martijn Pieters

Antworten:

1167

Ab Python 2.6 können Sie Folgendes verwenden ast.literal_eval:

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

Dies ist sicherer als die Verwendung eval. Wie seine eigenen Dokumente sagen:

>>> Hilfe (ast.literal_eval)
Hilfe zur Funktion literal_eval im Modul ast:

literal_eval (node_or_string)
    Bewerten Sie sicher einen Ausdrucksknoten oder eine Zeichenfolge, die einen Python enthält
    Ausdruck. Die angegebene Zeichenfolge oder der angegebene Knoten darf nur aus den folgenden Elementen bestehen
    Python-Literalstrukturen: Zeichenfolgen, Zahlen, Tupel, Listen, Diktate, Boolesche Werte,
    und keine.

Zum Beispiel:

>>> 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
quelle
Ich sollte hinzufügen, dass Sie die Zeichenfolge für die Verwendung mit ast.literal_eval bereinigen müssen. (Stellen Sie sicher, dass Anführungszeichen / doppelte Anführungszeichen in der Zeichenfolge maskiert sind)
Paulo Matos
Ich erhalte diesen Fehler Ich bin auf Python 2.6 (x86) unter Windows 7 x64 Datei "D: \ Python26 \ lib \ ast.py", Zeile 48, in literal_eval node_or_string = parse (node_or_string, mode = 'eval') Datei "D. : \ Python26 \ lib \ ast.py ", Zeile 36, in parse return compile (Ausdruck, Dateiname, Modus, PyCF_ONLY_AST) Datei" <unbekannt> ", Zeile 1 ^ SyntaxError: ungültige Syntax
Was ist mit "dict(a=1)"Style-Strings?
n611x007
Dies scheint für den Enum-Wert in einem Wörterbuch nicht zu funktionieren. Beispiel: d = "{'col': <Colors.RED: 2>, 'val': 2}"
shivshnkr
3
Warum nicht json.dumps und json.loads insead verwenden? Ich fand diese Lösung höher als mit eval
Auros132
232

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

JSON kann dieses Problem lösen, obwohl sein Decoder doppelte Anführungszeichen um Schlüssel und Werte wünscht. Wenn Ihnen ein Ersatz-Hack nichts ausmacht ...

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

HINWEIS: Wenn Sie einfache Anführungszeichen als Teil Ihrer Schlüssel oder Werte haben, schlägt dies aufgrund einer nicht ordnungsgemäßen Zeichenersetzung fehl. Diese Lösung wird nur empfohlen, wenn Sie eine starke Abneigung gegen die Bewertungslösung haben.

Weitere Informationen zu json-Anführungszeichen: jQuery.parseJSON löst den Fehler "Ungültiger JSON" aus, da in JSON ein einfaches Anführungszeichen enthalten ist

0x539
quelle
12
{"foo": "b'ar"}
Mark E. Haase
4
{'foo': (1, 2, 3)}
Mark E. Haase
1
Ich habe nach dieser Lösung gesucht. +1zum Informieren, dass der Decoder doppelte Anführungszeichen um Schlüssel und Werte wünscht.
H8pathak
Ein weiteres Problem ist für "{0: 'Hello'}".
Finn Årup Nielsen
3
Dies schlägt auch fehl, wenn Sie nachgestellte Kommas haben (nicht JSON-konform), z. B.: "{'Muffin': 'lolz', 'foo': 'kitty',}"
Guival
159

mit 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
quelle
13
Ich glaube nicht, dass es die Antwort des OP beantwortet. Wie konvertieren wir mit json.laads einen String s = "{'muffin': 'lolz', 'foo': 'kitty'}" zum Diktieren?
Technazi
Warum ist dieses Drucken 'u' in der Ausgabe? zB - str = '{"1": "P", "2": "N", "3": "M"}' d = json.loads (str) print d Ausgabe ist: {u'1 ': u'P ', u'3': u'M ', u'2': u'N '}
user905
2
@technazi: json.loads (h.replace ("'",' "'))
ntg
Es gibt jedoch Grenzen, z. B.: H = '{"Muffin": "lolz", "foo": "kitty",}', auch h = '{"muffins": "lolz", "foo": "kitty" "} ', (habe gerade einen Teil der gleichen Kommentare in einer ähnlichen Antwort bemerkt ... der Vollständigkeit
halber
4
Meiner Meinung nach ist das der kürzeste und einfachste Weg ... Auf jeden Fall der, den ich persönlich bevorzuge.
Nostradamus
35

Zum Beispiel von OP:

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

Wir können Yaml verwenden , um mit dieser Art von nicht standardmäßigem JSON in Zeichenfolgen umzugehen:

>>> import yaml
>>> s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> s
"{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> yaml.load(s)
{'muffin': 'lolz', 'foo': 'kitty'}
lqhcpsgbl
quelle
1
Dies führt dazu, dass "Ja" - und "Nein" -Strings in True / False konvertiert werden
Eric Marcos
23

Wenn der Zeichenfolge immer vertraut werden kann, können Sie sie verwenden eval(oder literal_evalwie vorgeschlagen verwenden; es ist sicher, egal was die Zeichenfolge ist). Andernfalls benötigen Sie einen Parser. Ein JSON-Parser (wie simplejson) würde funktionieren, wenn er nur Inhalte speichert, die zum JSON-Schema passen.

Blixt
quelle
8
Ab 2.6 ist simplejson als json-Modul in der Python-Standardbibliothek enthalten.
Eli Courtwright
11
Ja, das ist eine gute Antwort, aber beachten Sie, dass JSON offiziell keine Zeichenfolgen in einfachen Anführungszeichen unterstützt, wie im Beispiel des Originalplakats angegeben.
Ben Hoyt
19

Verwenden Sie json. Die astBibliothek verbraucht viel Speicher und ist langsamer. Ich habe einen Prozess, der eine Textdatei von 156 MB lesen muss. Astmit 5 Minuten Verzögerung für das Konvertierungswörterbuch jsonund 1 Minute mit 60% weniger Speicher!

Rogerio Silveira
quelle
13
hat aber seine Grenzen: versuchen Sie den String "{'foo': 'bar',}" zu
konvertieren
12

Zusammenfassen:

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

Ergebnisse:

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

Fazit: lieber json.loads

Anatoly Alekseev
quelle
5
Nur dass dies mit seiner Zeichenfolge in einfachen Anführungszeichen nicht funktioniert, was Teil seines anfänglichen Problems war. Leistung wurde nie erwähnt.
Michael Campbell
1
Wow .... Super Erklärung ....
Schlag Kirsche
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
quelle
3
Viele Fehler in diesem Ansatz. Was , wenn der Wert eines Schlüssels enthält {oder }. Was passiert , wenn es eingebettet ist dict. Was ist, wenn Wert enthält ,?
Om Sao
4

Es werden keine Bibliotheken verwendet:

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

HINWEIS: Da es fest codiert split("'")ist, funktioniert es nur für Zeichenfolgen, bei denen Daten in einfachen Anführungszeichen stehen.

tamerlaha
quelle