lector de expresiones aritméticas en python

I am trying to make an expression reader in python to compute basic arithmetic.

given the expression subtract(4,add(4,times(3,4))) --> -12

What would be the most pythonic way to build this? My method would be to convert the expression to a string, then create many if statements or switch cases to find keywords such as add,subtracto times. Entonces lee el (, read an integer and comma, and then run the if statement/switch case again. If a ) is ever encountered, that is when compute the required arithmetic of the latest key work. Pretty much storing key works and integers in a queue and computing the latest subtract,timeso add in the queue when ) is found. This to me seems a bit too much. I was wondering if there are any useful built in functions in python that would make the code pythonic or easier to read

preguntado el 27 de noviembre de 13 a las 01:11

@mgilson Looks like its evaluated like this 4-(4+(3*4)) -

Parse the text with ast.parse -

Is your expression language actually a proper subset of Python expression syntax? If so, ast.parse is the right way to do it (as JBernardo suggests), and eval is the quick&dirty&dangerous way (as gnibbler suggests). If not, you should write a parser. Pick a parser-writing framework, like analizando, start playing with it, and if you don't like the way it works, try another one. -

@dawg: Since his syntax is Python-style prefix function calls, not infix operators, the shunting-yard algorithm isn't going to be very helpful. -

2 Respuestas

Here's some working code to get you started using ast:

import ast

s = 'subtract(4,add(4,times(3,4)))'

# Probably better to use functions from the operator module here :-)
functions = {'subtract': lambda a,b: a-b,
             'add': lambda a, b: a+b,
             'times': lambda a,b: a*b}


def _evaluate(node):
    if isinstance(node, ast.Num):
        return node.n
    elif isinstance(node, ast.Name):
        return functions[node.id]
    elif isinstance(node, ast.Call):
        function = _evaluate(node.func)
        return function(*[_evaluate(n) for n in node.args])
    else:
        raise ValueError('Unknown node type: %s', type(node))


def evaluate(s):
    tree = ast.parse(s)
    node = tree.body[0].value
    return _evaluate(node)

print evaluate(s)

respondido 27 nov., 13:01

I compiled you code and it works but I am having trouble learning how to use ast. The documentation is hard for me to understand - bailarín del león

Pythonic way is to just write functions, subtract, add, times etc. Then pass to eval. Keep in mind all the dangers of passing arbitrary strings to eval

>>> from operator import add, sub, mul
>>> subtract = sub
>>> times = mul
>>> eval("subtract(4,add(4,times(3,4)))")
-12

If you can't trust the content of the strings, you'll have to weigh up trying to sanitise the strings, vs writing an engine on top of ast.parse

respondido 27 nov., 13:01

Your comment "Keep in mind all the dangers of passing arbitrary strings to eval" reminds me of the Seinfeld episode where they repeatedly say "Not that there's anything wrong with that!" You knew if you didn't add that sentence, someone would tell you it's wrong to use eval. Oh, politics. - SethMMorton

@SethMMorton, really using eval should be treated the same as importing a module. If you wouldn't blindly import a module from that person, don't eval the strings they give you. - Juan La Rooy

I would add that eval's signature is eval(source, globals, locals), so if some kind of end-user will be writing these formulas it may be a little safer to write something like:eval("subtract(4,add(4,times(3,4)))", {}, {'subtract':sub, 'add':add ...}). Of course, there may be other security concerns too, but this at least ensures the formulas wouldn't have access to every variable in your program. - Jeremy

@Jeremy: eval(..., {}, ...) can be hacked too. - unutbu

@unutbu agreed, that's why I said "there may be other security concerns", I should have just said "there ARE". I was already thinking about import as a liability. Great link. - Jeremy

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.