次の文字列のようなstr
のdict
表現をdict
に変換するにはどうすればよいですか?
s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
私はeval
を使わないことを好みます。他に何が使えますか?
この主な理由は、彼が書いた私の同僚のクラスの1つで、すべての入力を文字列に変換します。私はこの問題に対処するために彼のクラスを行ったり修正したりする気がしていません。
Python 2.6からは組み込みの ast.literal_eval
を使うことができます。
>>> import ast
>>> ast.literal_eval("{'muffin' : 'lolz', 'foo' : 'kitty'}")
{'muffin': 'lolz', 'foo': 'kitty'}
これはeval
を使うよりも安全です。それ自身の文書が言うように:
>>> help(ast.literal_eval) モジュールast内の関数literal_evalに関するヘルプ: literal_eval(node_or_string) 式を安全に評価するnodeまたはPython 式を含む文字列提供される文字列またはノードは、以下の Pythonリテラル構造:文字列、数字、タプル、リスト、辞書、ブール値、 、およびNoneのみで構成されます。
例えば:
>>> 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はこの問題を解決することができますが、デコーダはキーと値を二重引用符で囲む必要があります。あなたが置き換えハックを気にしないのであれば...
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'}
キーや値の一部として一重引用符があると、不適切な文字の置き換えが原因で失敗します。このソリューションは、evalソリューションに強い嫌悪感がある場合にのみ推奨されます。
Jsonの一重引用符の詳細: JSON応答におけるjQueryの一重引用符
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'>
OPの例では:
s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
Yaml を使って、このような非標準のjsonを文字列で扱うことができます。
>>> import yaml
>>> s = "{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> s
"{'muffin' : 'lolz', 'foo' : 'kitty'}"
>>> yaml.load(s)
{'muffin': 'lolz', 'foo': 'kitty'}
文字列が always に信頼できる場合は、eval
を使用できます(または推奨されるようにliteral_eval
を使用できます。文字列に関係なく安全です)。それ以外の場合は、パーサーが必要です。 JSONパーサー(simplejsonなど)は、JSON方式に適合するコンテンツを保存したことがあれば機能します。
json
を使用してください。 ast
ライブラリは大量のメモリを消費しますが遅くなります。 156Mbのテキストファイルを読む必要があるプロセスがあります。 Ast
は変換辞書json
に5分遅れ、60%少ないメモリを使用して1分遅れます。
これを試すことができます。
>>> 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'
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
Python 2.6を使用できない場合は、 http://code.activestate.com/recipes/364469/ のような単純な安全な方法を使用できます。
これはPythonコンパイラに便乗しているので、すべての総作業を自分で行う必要はありません。
要約する:
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('')
結果:
*** 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)
結論:json.loadsを好む
いかなるライブラリも使用されません。
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))
注:ハードコードされているので、split("'")
はデータが "一重引用符で囲まれている"文字列に対してのみ機能します。