J'essaie de lire une représentation sous forme de chaîne d'un tuple à partir d'un fichier et d'ajouter le tuple à une liste. Voici le code pertinent.
raw_data = userfile.read().split('\n')
for a in raw_data :
print a
btc_history.append(ast.literal_eval(a))
Voici la sortie:
(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
ast.literal_eval
(situé dans ast.py
) analyse l'arbre avec ast.parse
d'abord, puis il évalue le code avec une fonction récursive assez laide, interprétant les éléments de l'arbre d'analyse et les remplaçant par leurs équivalents littéraux. Malheureusement, le code n'est pas du tout extensible, donc pour ajouter Decimal
au code, vous devez copier tout le code et recommencer.
Pour une approche un peu plus simple, vous pouvez utiliser ast.parse
module pour analyser l'expression, puis le ast.NodeVisitor
ou ast.NodeTransformer
pour garantir qu'il n'y a pas de syntaxe indésirable ou d'accès aux variables indésirable. Compilez ensuite avec compile
et eval
pour obtenir le résultat.
Le code est un peu différent de literal_eval
en ce que ce code utilise en fait eval
, mais à mon avis est plus simple à comprendre et il n'est pas nécessaire de creuser trop profondément dans les arbres AST. Il autorise spécifiquement certains syntaxe, interdisant explicitement par exemple les lambdas, les accès aux attributs (foo.__dict__
est très diabolique), ou accède à des noms qui ne sont pas considérés comme sûrs. Il analyse votre expression très bien, et en plus j'ai également ajouté Num
(float et entier), list et dictionnaire littéraux.
Fonctionne de même sur 2.7 et 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)
De la documentation pour ast.literal_eval()
:
Évaluez en toute sécurité un nœud d'expression ou une chaîne contenant une expression Python. La chaîne ou le nœud fourni ne peut être composé que des structures littérales Python suivantes: chaînes, nombres, tuples, listes, dict, booléens et Aucun.
Decimal
n'est pas sur la liste des choses autorisées par ast.literal_eval()
.
Je sais que c'est une vieille question, mais je pense avoir trouvé une réponse très simple, au cas où quelqu'un en aurait besoin.
Si vous mettez des guillemets dans votre chaîne ("'bonjour'"), ast_literaleval () le comprendra parfaitement.
Vous pouvez utiliser une fonction simple:
def doubleStringify(a):
b = "\'" + a + "\'"
return b
Ou probablement plus approprié pour cet exemple:
def perfectEval(anonstring):
try:
ev = ast.literal_eval(anonstring)
return ev
except ValueError:
corrected = "\'" + anonstring + "\'"
ev = ast.literal_eval(corrected)
return ev