Поддержка ast.literal_eval() для набора литералов в Python 2.7?
В документе Whats New in Python 2.7 говорится, что поддержка набора литералов была перенесена из Python 3.1. Однако, похоже, эта поддержка не была расширена до функции ast
module literal_eval()
, как показано ниже.
Был ли это преднамеренный, надзор или что-то еще - и каковы самые чистые обходные пути для создания литерального набора из строкового представления? (Я предполагаю, что следующие работы в Python 3.1+, правильно?)
import ast
a_set = {1,2,3,4,5}
print a_set
print ast.literal_eval('{1,2,3,4,5}')
Выход с сообщением об ошибке:
set([1, 2, 3, 4, 5])
Traceback (most recent call last):
File "...\setliterals.py", line 4, in <module>
print ast.literal_eval('{1,2,3,4,5}')
File "...\Python\lib\ast.py", line 80, in literal_eval
return _convert(node_or_string)
File "...\Python\lib\ast.py", line 79, in _convert
raise ValueError('malformed string')
ValueError: malformed string
P.S. Единственным обходным решением, которое я могу придумать, является использование eval()
.
Ответы
Ответ 1
Из отчета об ошибке: http://bugs.python.org/issue10091
Раймонд Хеттингер говорит:
ast.literal_eval docs:
'Приведенная строка или node может состоять только из следующего Python литеральные структуры: строки, числа, кортежи, списки, dicts, booleans, и None. '
Я считаю, что из этого документа можно сделать вывод, что вопрос не обязательно является ошибкой, поскольку набор литералов был передан из Python 3.2 в 3.1 и 2.7. Это то, о чем должен знать пользователь Python 2.7 ast.literal
.
Ответ 2
Я использовал это для преобразования столбцов в pandas DataFrame (df[col] = df[col].apply(to_set)
). Может быть полезно для любого, кто найдет этот вопрос. Это может быть не так быстро, но он избегает использования eval
.
def to_set(set_str):
"""
Required to get around the lack of support for sets in ast.literal_eval.
It works by converting the string to a list and then to a set.
Parameters
----------
set_str : str
A string representation of a set.
Returns
-------
set
Raises
------
ValueError
"malformed string" if the string does not start with '{' and and end
with '}'.
"""
set_str = set_str.strip()
if not (set_str.startswith('{') and set_str.endswith('}')):
raise ValueError("malformed string")
olds, news = ['{', '}'] , ['[',']']
for old, new in izip(olds, news):
set_str = set_str.replace(old, new)
return set(literal_eval(set_str))