Неправильная строка ValueError ast.literal_eval() со строковым представлением кортежа
Я пытаюсь прочитать строковое представление кортежа из файла и добавить кортеж в список. Вот соответствующий код.
raw_data = userfile.read().split('\n')
for a in raw_data :
print a
btc_history.append(ast.literal_eval(a))
Вот вывод:
(Decimal('11.66985'), Decimal('0E-8'))
Traceback (most recent call last):
File "./goxnotify.py", line 74, in <module>
main()
File "./goxnotify.py", line 68, in main
local.load_user_file(username,btc_history)
File "/home/unix-dude/Code/GoxNotify/local_functions.py", line 53, in load_user_file
btc_history.append(ast.literal_eval(a))
File "/usr/lib/python2.7/ast.py", line 80, in literal_eval
return _convert(node_or_string)
`File "/usr/lib/python2.7/ast.py", line 58, in _convert
return tuple(map(_convert, node.elts))
File "/usr/lib/python2.7/ast.py", line 79, in _convert
raise ValueError('malformed string')
ValueError: malformed string
5 ответов
ast.literal_eval
(находится в ast.py
) разбирает дерево с ast.parse
сначала он оценивает код довольно уродливой рекурсивной функцией, интерпретируя элементы дерева разбора и заменяя их их буквальными эквивалентами. К сожалению, код совсем не расширяемый, поэтому добавить Decimal
к коду нужно скопировать весь код и начать все сначала.
Для немного более легкого подхода вы можете использовать ast.parse
модуль для анализа выражения, а затем ast.NodeVisitor
или же ast.NodeTransformer
чтобы гарантировать отсутствие нежелательного синтаксиса или нежелательных обращений к переменным. Затем скомпилируйте с compile
а также eval
чтобы получить результат.
Код немного отличается от literal_eval
в том, что этот код на самом деле использует eval
, но, на мой взгляд, это проще понять, и не нужно слишком глубоко копаться в деревьях AST. В частности, он допускает только некоторый синтаксис, явно запрещающий, например, лямбды, доступ к атрибутам (foo.__dict__
очень злой), или доступ к любым именам, которые не считаются безопасными. Это хорошо анализирует ваше выражение, и в качестве дополнительного я также добавил Num
(float и integer), список и словарь литералов.
Также работает одинаково на 2.7 и 3.3
import ast
import decimal
source = "(Decimal('11.66985'), Decimal('1e-8'),"\
"(1,), (1,2,3), 1.2, [1,2,3], {1:2})"
tree = ast.parse(source, mode='eval')
# using the NodeTransformer, you can also modify the nodes in the tree,
# however in this example NodeVisitor could do as we are raising exceptions
# only.
class Transformer(ast.NodeTransformer):
ALLOWED_NAMES = set(['Decimal', 'None', 'False', 'True'])
ALLOWED_NODE_TYPES = set([
'Expression', # a top node for an expression
'Tuple', # makes a tuple
'Call', # a function call (hint, Decimal())
'Name', # an identifier...
'Load', # loads a value of a variable with given identifier
'Str', # a string literal
'Num', # allow numbers too
'List', # and list literals
'Dict', # and dicts...
])
def visit_Name(self, node):
if not node.id in self.ALLOWED_NAMES:
raise RuntimeError("Name access to %s is not allowed" % node.id)
# traverse to child nodes
return self.generic_visit(node)
def generic_visit(self, node):
nodetype = type(node).__name__
if nodetype not in self.ALLOWED_NODE_TYPES:
raise RuntimeError("Invalid expression: %s not allowed" % nodetype)
return ast.NodeTransformer.generic_visit(self, node)
transformer = Transformer()
# raises RuntimeError on invalid code
transformer.visit(tree)
# compile the ast into a code object
clause = compile(tree, '<AST>', 'eval')
# make the globals contain only the Decimal class,
# and eval the compiled object
result = eval(clause, dict(Decimal=decimal.Decimal))
print(result)
Из документации для ast.literal_eval()
:
Безопасно оцените узел выражения или строку, содержащую выражение Python. Предоставленная строка или узел могут состоять только из следующих литеральных структур Python: строк, чисел, кортежей, списков, диктов, логических значений и None.
Decimal
не в списке вещей, разрешенных ast.literal_eval()
,
в моем случае я решил с:
my_string = my_string.replace(':false', ':False').replace(':true', ':True')
ast.literal_eval(my_string)
Использовать eval()
вместо того ast.literal_eval()
если входу доверяют (что в вашем случае).
raw_data = userfile.read().split('\n')
for a in raw_data :
print a
btc_history.append(eval(a))
Это работает для меня в Python 3.6.0
Я знаю, что это старый вопрос, но я думаю, что нашел очень простой ответ, если он кому-то понадобится.
Если вы поместите строковые кавычки внутри своей строки ("'hello'"), ast_literaleval() прекрасно это поймет.
Вы можете использовать простую функцию:
def doubleStringify(a):
b = "\'" + a + "\'"
return b
Или, возможно, более подходит для этого примера:
def perfectEval(anonstring):
try:
ev = ast.literal_eval(anonstring)
return ev
except ValueError:
corrected = "\'" + anonstring + "\'"
ev = ast.literal_eval(corrected)
return ev