note.nkmk.me

Pythonのast.literal_eval()で文字列をリストや辞書に変換

Date: 2018-09-09 / tags: Python, 文字列操作, リスト, 辞書

Pythonで文字列(str)をリスト(list)や辞書(dict)に変換するにはastモジュールのliteral_eval()を使う。

ここでは、文字列を区切り文字で分割してリスト化するsplit()メソッドについて、および、ast.literal_eval()eval(), json.loads()との違いについても説明する。

  • 文字列を区切り文字などで分割してリスト化
    • 'a, b, c''1 2 3'のような文字列が対象
  • ast.literal_eval()で文字列をリストや辞書に変換
    • '["a", "b", "c"]''{"key1": 1, "key2": 2}'のような文字列が対象
  • 組み込み関数eval()ast.literal_eval()の違い
  • json.loads()ast.literal_eval()の違い
スポンサーリンク

文字列を区切り文字などで分割してリスト化

特定の文字で区切られた文字列はsplit()メソッドで分割してリストオブジェクトに変換できる。

空白ありのカンマで区切られた例。

s = 'a, b, c'

l = s.split(', ')
print(l)
# ['a', 'b', 'c']

print(type(l))
# <class 'list'>

split()で分割した結果は文字列を要素とするリストとなる。数字もintなどの数値型ではなく文字列となる。

数値のリストにしたい場合は文字列を数値に変換するint()float()とリスト内包表記を組み合わせる。

s = '1-2-3'

l = s.split('-')
print(l)
# ['1', '2', '3']

print(type(l[0]))
# <class 'str'>

l = [int(c) for c in s.split('-')]
print(l)
# [1, 2, 3]

print(type(l[0]))
# <class 'int'>

split()のほか、改行で分割(行ごとに分割)するsplitlines()や正規表現で分割するre.split()などもある。詳細は以下の記事を参照。

ast.literal_eval()で文字列をリストや辞書に変換

特定の文字で区切られた文字列ではなく、Pythonのコード上での書き方で記述された文字列をリストや辞書に変換するにはast.literal_eval()を使う。

astモジュールをインポートする。標準ライブラリに含まれているので追加のインストールは必要ない。

import ast

s = '["a", "b", "c"]'

l = ast.literal_eval(s)
print(l)
# ['a', 'b', 'c']

print(type(l))
# <class 'list'>

ast.literal_eval()は文字列をPythonのリテラルとして評価するので、数値やブール値などを表現する文字列をそのままその型の値に変換してくれる。

s = '["x", 1, True]'

l = ast.literal_eval(s)
print(l)
# ['x', 1, True]

print(type(l[0]))
# <class 'str'>

print(type(l[1]))
# <class 'int'>

print(type(l[2]))
# <class 'bool'>

ast.literal_eval()が変換するのは以下のリテラル。

与えられる文字列またはノードは次のリテラルのみからなるものに限られます: 文字列、バイト列、数、タプル、リスト、辞書、集合、ブール値、 None 。
33.2. ast.literal_eval() --- 抽象構文木 — Python 3.7.0 ドキュメント

辞書(dict型)や集合(set型)を表す文字列を変換する例は以下の通り。

s = '{"key1": 1, "key2": 2}'

d = ast.literal_eval(s)
print(d)
# {'key1': 1, 'key2': 2}

print(type(d))
# <class 'dict'>

s = '{1, 2, 3}'

se = ast.literal_eval(s)
print(se)
# {1, 2, 3}

print(type(se))
# <class 'set'>

ast.literal_eval()はPythonのオブジェクトを一旦テキストファイルなどに文字列として保存して、再度オブジェクトとして読み込みたい場合などに便利。

テキストファイルなどのような人間が読める形式である必要がなければpickleで保存する方法もある。

組み込み関数eval()とast.literal_eval()の違い

ast.literal_eval()と似たような関数に組み込み関数eval()がある。

違いは以下の通り。

  • ast.literal_eval(): リテラルのみを含む式を評価
  • eval(): リテラルに加え変数およびそれらの演算を含んだ式を評価

eval()+による加算を評価できるが、ast.literal_eval()だとエラー。

s = '["x", 1 + 10]'

print(eval(s))
# ['x', 11]

# print(ast.literal_eval(s))
# ValueError: malformed node or string

eval()は別途定義された変数を評価できるが、ast.literal_eval()だとエラー。

a = 100
print(eval('[1, a]'))
# [1, 100]

# a = 100
# print(ast.literal_eval('[1, a]'))
# ValueError: malformed node or string

ast.literal_eval()は対象をリテラルのみに限定しているのでeval()よりも安全。実際は変数や演算を含む文字列を評価してオブジェクトに変換するユースケースはあまりないので、基本的にはast.literal_eval()を使うべき。

json.loads()とast.literal_eval()の違い

JSON形式の文字列をリストや辞書からなるオブジェクトに変換する関数としてjson.loads()がある。jsonモジュール(標準ライブラリ)をインポートして使う。

import json

s = '{"key1": [1, 2, 3], "key2": "abc"}'

print(json.loads(s))
# {'key1': [1, 2, 3], 'key2': 'abc'}

print(ast.literal_eval(s))
# {'key1': [1, 2, 3], 'key2': 'abc'}

json.loads()はあくまでもJSON形式の文字列が対象なのでJSONの仕様から外れた文字列は変換できない。

値は、2重引用符に囲まれた文字列、数値、true、false、null、オブジェクト、配列です。これらの構造は、ネストできます。
https://www.json.org/json-ja.html

True, False, Noneast.literal_eval()では変換できるが、json.loads()では変換できない。

s = '[True, False, None]'

# print(json.loads(s))
# JSONDecodeError: Expecting value:

print(ast.literal_eval(s))
# [True, False, None]

逆にtrue, false, nulljson.loads()では変換できるが、ast.literal_eval()では変換できない。

s = '[true, false, null]'

print(json.loads(s))
# [True, False, None]

# print(ast.literal_eval(s))
# ValueError: malformed node or string

また、json.loads()では、JSONの仕様の通り文字列は二重引用符(ダブルクォーテーション)"で囲まれている必要があるが、ast.literal_eval()ではPythonの文字列リテラルの仕様の通り、一重引用符(シングルクォーテーション)'でも三重引用符(トリプルクォーテーション)'''または"""でもOK。

s = "{'key1': 'abc', 'key2': '''xyz'''}"

# print(json.loads(s))
# JSONDecodeError: Expecting property name enclosed in double quotes

print(ast.literal_eval(s))
# {'key1': 'abc', 'key2': 'xyz'}

JSONを扱う場合はjsonモジュールを使ったほうが便利。JSON形式のファイルを読み込むjson.load()もある。詳細は以下の記事を参照。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事