I am learning Python from interactivepython.org. On this site, they have the code for evaluating postfix expressions.. but I would like to see how it would be done for prefix expressions as well. Here is the code:
def postfixEval(postfixExpr):
operandStack = Stack()
tokenList = postfixExpr.split()
for token in tokenList:
if token in "0123456789":
operandStack.push(int(token))
else:
operand2 = operandStack.pop()
operand1 = operandStack.pop()
result = doMath(token,operand1,operand2)
operandStack.push(result)
return operandStack.pop()
def doMath(op, op1, op2):
if op == "*":
return op1 * op2
elif op == "/":
return op1 / op2
elif op == "+":
return op1 + op2
else:
return op1 - op2
print(postfixEval('7 8 + 3 2 + /'))
If I understand this lesson correctly, would I just change the operand ordering?
No, the operands come in the same order. The difference is that you have to take the operation before the operands instead of afterward. This is complicated by the fact that you likely have to make recursive calls to evaluate the operands: if an operand begins with an operation, you recur.
You can write a similar parser for prefix notation arithmetic, but it's going to be more complicated because the logic of what to do when you've received a number token is more complicated (sometimes you store it, other times you evaluate an operator and either store the result or evaluate another operator, etc.). It's often easier to use recursion, since the function call stack can handle this more easily than a stack you have to manage by hand.
Here's an implementation that uses recursion (and an iterator to handle the tokens as a stream):
def prefix_eval(prefix_expr):
return prefix_eval_rec(iter(prefix_expr.split()))
def prefix_eval_rec(expr_iter):
token = next(expr_iter)
if token.isdigit():
return int(token)
op1 = prefix_eval_rec(prefix_iter)
op2 = prefix_eval_rec(prefix_iter)
return doMath(token, op1, op2)
Here's a quick attempt to do this without recursion. It's quite a bit messier than the postfix code, since we need to know how many arguments the top operator on the stack has received so far. My solution is to use a list for the operator and its arguments and check its length. The current_op list is conceptually on the top of the stack. Your could instead use a more conventional stack with individual items on it, but you'd need to be able to inspect the type of the top item on the stack (hopefully without popping and re-pushing it).
def prefix_eval(prefix_expr):
token_list = prefix_expr.split()
stack = Stack()
current_op = []
for token in token_list:
if token.isdigit():
current_op.append(int(token))
while len(current_op) == 3:
result = doMath(*current_op)
current_op = stack.pop()
current_op.append(result)
else:
stack.push(current_op)
current_op = [token]
return current_op[0]
Related
If I enter input with parentheses it resolves only what is inside the parentheses and not what is after or before, if I enter two expressions with parentheses it returns None.
you can see it in the code.
def divide(a, b):
return a/b
def pow(a, b):
return a**b
def addA(a, b):
return a+b
def subA(a, b):
return a-b
def mul (a, b):
return a*b
operators = {
'+': addA,
'-': subA,
'*': mul,
'/': divide,
'^' : pow,
}
def calculate(s):
if s.isdigit():
return float(s)
elif '[' in s:
start = s.index('[')
end = s.rindex(']')
return calculate(s[start + 1:end])
for c in operators.keys():
left, operator, right = s.partition(c)
if operator in operators:
return operators[operator](calculate(left), calculate(right))
calc = input("Type calculation:\n")
print("Answer: " + str(calculate(calc)))
input: [2+2]+[2+2]
output: None
input [2+3]*2
output 5
Nice idea.
elif '[' in s:
...
return calculate(s[start + 1:end])
Beautifully simple.
But, alas, wrong.
Consider 1+1+1+[8+9]+2+2+2.
Is there a [ bracket in there? Yes.
And the recursive call correctly computes 17.
But we're ignoring the 1's and the 2's.
Also, when the for finishes and we fall of the
end of the function we return None.
Consider doing something else in that case,
perhaps raise a fatal error.
You probably want to invert the order of operations,
so higher priority ops will bind more tightly.
You will find you can iterate through the edit - debug
cycle much more quickly if you write some simple
unit tests
that lock in "working" behavior. Only the last test fails ATM.
class CalculateTest(unittest.TestCase):
def test_addition(self):
self.assertEqual(5, calculate("2+3"))
self.assertEqual(5, calculate("[2+3]"))
self.assertEqual(9, calculate("2+3+4"))
self.assertEqual(9, calculate("2+[3+4]"))
I found it convenient to have the function always return a float.
Once you have this function in better shape, please
let us know
how it turned out.
The calculate function took into account the first and last parenthesis, skipping the middle ones.
Try the following:
def extract_expression(s):
start = s.rindex('[')
end = s.rindex(']')
return s[start + 1:end]
def calculate(expr):
for c in operators.keys():
left, operator, right = expr.partition(c)
if operator in operators:
return operators[operator](float(left), float(right))
def solution(s):
while '[' in s:
sub_expression = extract_expression(s)
result = calculate(sub_expression)
s = s.replace('[' + sub_expression + ']', str(result))
if s.isdigit():
return float(s)
return calculate(s)
calc = input("Type calculation:\n")
print("Answer: " + str(solution(calc)))
input: [2+2]+[2+2],
output: 8.0
input: [2+3]*2,
output: 10.0
input: 2+[2+2],
output: 6.0
I am a beginner to Python. I have tried learning python and C++ in the past had learnt about like classes and stuff but then had to abandon it for reasons, now I am learning python from the beginning as I have forgotten it all.
So I was trying to make a calculator like the ones you have in mobile using python but without any GUI. Now, the problem I am having right now is, see in your mobile calculator you can do one operation after the other i.e. say you typed 95+8x2, that mobile calculator will have no problem in deducing operator precedence from your input and give result as 111 and I am trying to do something similar.
But the problem is, the way I know how to do it as a beginner would require a lot of code, it would not be complex but get too long and hence a lot of time wasted. Here is how I have thought of doing it right now :
Find the location of each of the operators in the input for that I am using their indexes i.e. I have named the input as 'alg_operation' and for example, I am using alg_operation.find('*') to where the multiplaction operator is, I am calling this index as location_prod using this I am able to calculate the product simply via converting the part of the string that comes before the operator into float then multiply it with the other part that comes just after (obviously converting it into float as well).
After finding the location of each of the 5 operators that I have decided to include i.e. exponentiation, multiplication, division (// not /), addition and subtraction, I would have to write code for 120 different cases of the arrangement of these operators, which may not be complex but definitely will take a lot of time.
How can I quickly deduce operator precedence from the string input ?
I will update this post if I learn anything new, since I am a beginner to programming.
You can indeed evaluate an arbitrary python expression with eval. Use of eval is a pretty big code smell, because it lets you execute anything, but for completeness it would be done like this:
expr = input("Please, please, please only write maths: ")
print(eval(exp))
note that you could type import shutil; shutil.rmtree("/home") and python would cheerfully run it. So obviously we don't want to do this.
We could try to protect ourselves by sanitising the input. In this case this might actually work, with something like:
safe_chars = (".", "*", "+", "/", "-"," ", *range(10))
if not all(x in safe_chars for x in expr):
raise ValueError("You tried to enter dangerous data!")
I can't immediately think of any way to do anything dangerous with input consisting only of those chars, but doubtless someone will point it out immediately in the comments [in which case I'll add it here]. More generally, sanitising data like this is hard, because in order to know what's safe you really need to understand the input, by which point you've just written a parser.
Please do note that eval is inherently dangerous. It can be a useful hack for once-off code, although even then... and it is of course useful when you actually want to evaluate python code.
Converting it to reverse polish notation will solve your problem
def readNumber(string: str,index: int):
number = ""
while index < len(string) and isNumber(string[index]):
number += string[index]
index += 1
return float(number), index
def isOperator(token):
operators = ['+', '-', '*', '/', '%']
return token in operators
def isNumber(token):
return (token >= '0' and token <= '9') or token == "."
def toRpn(string: str):
"""
Converts infix notation to reverse polish notation
"""
precedence = {
'(': 0,
'-': 1,
'+': 1,
'*': 2,
'/': 2,
'%': 2,
}
i = 0
fin = []
ops = []
while i < len(string):
token = string[i]
if isNumber(token):
number, i = readNumber(string,i)
fin.append(number)
continue
if isOperator(token):
top = ops[-1] if ops else None
if top is not None and precedence[top] >= precedence[token]:
fin.append(ops.pop())
ops.append(token)
i += 1
continue
if token == '(':
ops.append(token)
i += 1
continue
if token == ')':
while True:
operator = ops.pop()
if operator == '(':
break
fin.append(operator)
if not ops:
break
i += 1
continue
i += 1
while ops:
fin.append(ops.pop())
return fin
def calculate_rpn(rpn: list):
"""
Calculates the result of an expression in reverse polish notation
"""
stack = []
for token in rpn:
if isOperator(token):
a = stack.pop()
b = stack.pop()
if token == '+':
stack.append(b + a)
elif token == '-':
stack.append(b - a)
elif token == '*':
stack.append(b * a)
elif token == '/':
stack.append(b / a)
elif token == '%':
stack.append(b % a)
else:
stack.append(token)
return stack[0]
print ("90165: ", calculate_rpn(toRpn("27+38+81+48*33*53+91*53+82*14+96")))
print ("616222: ", calculate_rpn(toRpn("22*26*53+66*8+7*76*25*44+78+100")))
print (calculate_rpn(toRpn("(22+4)*4")))
My Github
You can easily add more operators and their precedence if you want.
You have to modify the precedence array and the isOperator function. Also you should modify the function of the respective operator in the calculate_rpn function.
I'm relatively new to Python programming so please forgive me for a noob question. I am trying to see if it is at all possible to call the operation methods (add, sub, mul, div).
The program is a simple calculator which takes a series of integers and operators and performs the calculation once "=" is pressed.
A few noted for clarity;
comb is a user generated list of numbers and operators as shown in the dictionary opsymbols.
the line beginning with comb[0] is a test, eventually I'll replace this with a more complete way of performing a calculation based on the comb length
The code I'm having trouble with is as follows:
def calculator():
opsymbols = {'+':add, '-':sub, '*':mul, '/':div}
for sym in opsymbols:
if sym == comb[1]:
opsymbols[sym]
print(sym) # check to see whether the IF logic works.
a = comb[0]
print(a) # check to see if assignment of "a" works
b = comb[2]
print(b) # check to see if assignment of "b" works
def add():
a = int()
b = int()
result = a + b
print("result =" + str(result))
def sub():
a = int()
b = int()
result = a - b
print("result =" + str(result))
def mul():
a = int()
b = int()
result = a * b
print("result =" + str(result))
def div():
a = int()
b = int()
result = a / b
print("result =" + str(result))
Do you mean that call?
opsymbols[sym]()
You have also problem with shadowing variables. But still you are looking for the solution mentioned above. You need to call func in dictionary using key sym and function operator ().
Just create a second dictionary with function pointers to each one of those functions. Alternatively, you can just run eval() and add the operation code in at runtime.
Method 1:
op_dict = {'+': add, '-': sub, '*': mult, '/': div } # dict with function pointers
op_dict[op]() # call the function
Method 2:
return eval('{num1}{op}{num2}'.format(num1=num1, op=op, num2=num2))
If you're only going to have these 4 operations and there's only 2 nums you're calculating, it might be easier to use Method 2 and have all of this done in the calculate function. It would look something like:
def calculate(input):
for op in ['+', '-', '*', '/']:
if op in input:
num1 = input.rsplit(op)[0]
num2 = input.rsplit(op)[-1]
return eval('{num1}{op}{num2}'.format(num1=num1, op=op, num2=num2))
Even if you have multiple operations with multiple input numbers, you can just do a split at each operation delimiter, and run the list of those calculations through the calculate function recursively
I'm working on a project to implement infix-to-postfix transformations in python.
The implementation of the code works as long as the expression is fully bracketed. It cannot handle expressions where humans would implicitly assume an order to the calculation.
For instance, I can use a fully-bracketed expression such as:
((3+15)*2)+(6-3)
And get the right result.
However, humans might normally write:
(3+15)*2+(6-3)
Where the first outer bracket is assumed.
Are there any algorithms that could correctly add brackets. If not, is there a best-practice solution for how to handle this sort of problem?
Update:
Here is the implementation of the parse tree function:
class BinaryTree:
def __init__(self, root):
self.key = root
self.left_child = None
self.right_child = None
def insert_left(self, new_node):
if self.left_child == None:
self.left_child = BinaryTree(new_node)
else:
t = BinaryTree(new_node)
t.left_child = self.left_child
self.left_child = t
def insert_right(self, new_node):
if self.right_child == None:
self.right_child = BinaryTree(new_node)
else:
t = BinaryTree(new_node)
t.right_child = self.right_child
self.right_child = t
def get_right_child(self):
return self.right_child
def get_left_child(self):
return self.left_child
def set_root_val(self, obj):
self.key = obj
def get_root_val(self):
return self.key
def build_parse_tree(fp_exp):
fp_list = re.findall('[+-/*//()]|\d+', fp_exp)
p_stack = Stack()
e_tree = BinaryTree('')
p_stack.push(e_tree)
current_tree = e_tree
for i in fp_list:
if i == '(':
current_tree.insert_left('')
p_stack.push(current_tree)
current_tree = current_tree.get_left_child()
elif i not in ['+', '-', '*', '/', ')']:
current_tree.set_root_val(int(i))
parent = p_stack.pop()
current_tree = parent
elif i in ['+', '-', '*', '/']:
current_tree.set_root_val(i)
current_tree.insert_right('')
p_stack.push(current_tree)
current_tree = current_tree.get_right_child()
elif i == ')':
current_tree = p_stack.pop()
else:
raise ValueError
return e_tree
def postorder(tree):
if tree != None:
postorder(tree.get_left_child())
postorder(tree.get_right_child())
print (tree.get_root_val())
The output from the second expression postorder:
3
6
15
2
3
-
+
The one with the first (correct) is:
3
15
+
6
2
3
-
+
Disclaimer: Sorry for that huge response; I was curious and just wrote down what I did during testing around a bit for your question. Find the entire code here: https://gist.github.com/jbndlr/3657fa890539d29c9e4b0311dc60835d
By the way, this is just test code and not meant to be used in production, as it may still be flawed.
Response
Your sequence parsing and tree setup with pushes of empty strings seems a bit odd, but I cannot accurately point to your error. Your parsing somehow swallows the * operator, probably because its left element is a closing bracket.
While I was playing around with this a bit, I tried to reproduce and came up with a solution that correctly parses simple equations and can generate the required parentheses. Even though no longer required, if the tree is already parsed correctly, you can use this to generate fully bracketed equations, or extend it by your own needs.
Preparation: The Imports
from __future__ import print_function
import re
Step 1: Tokenizing the Input
This function takes a string as an expression and generates a list of tuples representing your tokens. It also already classifies them as kind of simple (string-represented) types for later processing.
def tokenize(expression):
'''Generate tokens from a string following fixed rules.
'''
scanner = re.Scanner([
(r'[0-9]\.[0-9]+', lambda _, t: ('FLOAT', t)),
(r'[0-9]+', lambda _, t: ('INTEGER', t)),
(r'[a-z_]+', lambda _, t: ('IDENTIFIER', t)),
(r'\(', lambda _, t: ('P_OPEN', t)),
(r'\)', lambda _, t: ('P_CLOSE', t)),
(r'[+\-*/]', lambda _, t: ('OPERATOR', t)),
(r'\s+', None),
])
tokens, _ = scanner.scan(expression)
return tokens
This approach is by far not complete, but it is sufficient for demonstrating building binary parse trees. Note that the order of rules is important; it makes no difference here, as I do not catch single dots, but putting INTEGER before FLOAT could mess things up later.
Step 2: Parse the Hierarchy
The next function takes a list of tokens as generated in Step 1 and resolves all parts that are put into brackets as individual sub-lists. The result is a nested list where each previously bracketed part is shifted to a deeper level.
def parse(tokens, in_parens=False):
'''Parse a list of tokens that may contain brackets into a token hierarchy
where all brackets are removed and replaced by list nesting.
'''
cur = []
i = 0
while i < len(tokens):
t = tokens[i]
if t[0] == 'P_OPEN':
# If we hit an opening bracket, we memorize its position and search
# for the corresponding closing one by counting the stacked
# brackets.
pos_open = i
pos_close = None
par_stack = 0
for j, p in enumerate(tokens[i:]):
if p[0] == 'P_OPEN':
# Deeper nesting, increase.
par_stack += 1
elif p[0] == 'P_CLOSE':
# Level closed, decrease.
par_stack -= 1
if par_stack == 0:
# If we hit level 0, we found the corresponding closing
# bracket for the opening one.
pos_close = i + j
break
if pos_close is None:
# If we did not find a corresponding closing bracket, there
# must be some syntax error.
raise Exception('Syntax error; missing closing bracket.')
# For the bracketed subset we just found, we invoke a recursive
# parsing for its contents and append the result to our hierarchy.
elem = parse(tokens[i + 1:j], in_parens=True)
cur.append(elem)
i = j
elif t[0] == 'P_CLOSE':
if not in_parens:
# If we hit a closing bracket but are not searching for one, we
# found too many closing brackets, which is a syntax error.
raise Exception('Syntax error; too many closing brackets.')
return cur
else:
cur.append(t)
i += 1
return cur
This makes sure that we do not miss the explicit grouping given by parentheses in the expression. At the same time, as we count parenthesis levels, we can spot syntax errors that result from wrong bracket counts.
Step 3: Build a Tree
In order to proceed, we need to build an actual binary tree from our hierarchy. The hierarchy we got from Step 2 still has all un-bracketed chained operators on the same level, so we do not know yet about the order in which the operators need to be executed. This is what is solved now.
When creating a new Node from a hierarchy (i.e. a nested list of tokens), we search for a pivot element that we can use as the operator of the currently built Node. We choose the weakest binding operator, because we build the tree top-down, but it will be evaluated bottom-up. Hence, the operation that shall be performed last is the one we want to have in the upmost Node of our tree.
class Node(object):
def __init__(self, hierarchy, parent=None):
if len(hierarchy) == 1 and type(hierarchy[0]) is list:
hierarchy = hierarchy[0] # Bracketed descent
# Find position of operator that has the weakest binding priority and
# use it as pivot element to split the sequence at. The weakest binding
# is executed last, so it's the topmost node in the tree (which is
# evaluated bottom-up).
pivot = self._weakest_binding_position(hierarchy)
if pivot is not None:
self.left = Node(hierarchy[:pivot], parent=self)
self.op = hierarchy[pivot][1]
self.right = Node(hierarchy[pivot + 1:], parent=self)
else:
# There is no pivot element if there is no operator in our
# hierarchy. If so, we hit an atomic item and this node will be
# a leaf node.
self.value = hierarchy[0]
def _binding_order(self, operator):
'''Resolve operator to its binding order.'''
if operator in '+-':
return 1
elif operator in '*/':
return 2
raise Exception('Parsing error; operator binding cannot be assessed.')
def _weakest_binding_position(self, tokens):
'''Return position of operator with weakest binding (maintains LTR).'''
ops = sorted([
(i, self._binding_order(t[1]))
for i, t in enumerate(tokens)
if t[0] == 'OPERATOR'
], key=lambda e: e[1], reverse=True)
if len(ops) == 0:
if len(tokens) != 1:
raise Exception('Parsing error; found sequence w/o operator.')
return None
return ops[-1][0]
def isleaf(self):
if hasattr(self, 'value'):
return True
return False
def __str__(self):
if self.isleaf():
return str(self.value[1])
else:
return '({:s} {:s} {:s})'.format(self.left, self.op, self.right)
If you want to see how the tree is set up, just print(self) at the end of Node.__init__(). This will give you a bottom-up print of all nodes.
I added some parentheses in the Node.__str__() method to actually make a fully-bracketed expression from the input. You can verify with some samples like so:
if __name__ == '__main__':
expressions = [
'(3+15)*2+6-3',
'(a+15)*2+6/3'
]
for expr in expressions:
root = Node(parse(tokenize(expr)))
print(root)
... yields
>>> ((((3 + 15) * 2) + 6) - 3)
>>> (((a + 15) * 2) + (6 / 3))
So, if you want to print (or return) this in postfix notation now, you can just switch the operator and operands by changing this row in the Node.__str__() method:
<<<<<<<<
return '({:s} {:s} {:s})'.format(self.left, self.op, self.right)
======
return '({:s} {:s} {:s})'.format(self.left, self.right, self.op)
>>>>>>>>
If you want your postfix notation to be returned for further processing instead of just obtaining it as a string, just write another method like so (warning: pseudo-code):
def postfix(self):
if self.isleaf():
return self.value
else:
return (self.left.postfix(), self.right.postfix(), self.op)
and then invoke it from your root node:
pf = root.postfix()
Step 4: Evaluation
Finally, you can put a method into the Node class to evaluate the expression. This method checks whether or not we have a leaf node, and, if so, returns its value using the correct type. Otherwise, it evaluates its left and right child and applies the desired operator and then passes the result upwards the tree.
def eval(self, variables={}):
if self.isleaf():
ttype, value = self.value
if ttype == 'FLOAT':
return float(value)
elif ttype == 'INTEGER':
return int(value)
elif ttype == 'IDENTIFIER':
if value in variables.keys():
return variables[value]
else:
raise Exception('Unbound variable: {:s}'.format(value))
else:
raise Exception('Unknown type: {:s}'.format(ttype))
else:
left = self.left.eval(variables=variables)
right = self.right.eval(variables=variables)
if self.op == '+':
return left + right
elif self.op == '-':
return left - right
elif self.op == '*':
return left * right
elif self.op == '/':
return left / right
else:
raise Exception('Unknown operator: {:s}'.format(self.op))
Some special thing here is, that you can also use variables (like a in my example in Step 3), but you have to map them to actual (un-typed) values on evaluation:
if __name__ == '__main__':
expression = '(a+15)*2+6/3'
tokens = tokenize(expression)
hierarchy = parse(tokens)
root = Node(hierarchy)
print(root)
print(root.eval({'a': 7}))
... yields:
>>> (((a + 15) * 2) + (6 / 3))
>>> 46
Final Thoughts
As already stated, this is far from perfect. I even noticed, that it somehow fails to parse an expression, where a single operator connects two bracketed parts like (1-2)/(0+5) -- but I leave this to whoever wants to have a look at it ;)
Hope it helps somehow; and sorry for this huge response. I was just curious and had a little bit of spare time.
Recently, I got an interview question which says to convert string expressions like "1+2-3" and "-2+4" to 0 and 2 respectively. Assuming the inputs are single digits numbers followed by signs and no NULL input. I tried this output but the interviewer said I am close but not perfect solution. Please help me here. Thanks.
def ans(input):
result, j = 0, 0
for i in input:
if i == '+' or i == '-':
j = i
else:
i = int(i)
result = result j i
return result
ans("1+2-3")
ans("-2+4")
I am making some silly mistake but I am learning. Thanks in advance.
Two things need fixing to work at all:
You need to handle the initial value properly; when the initial value is non-negative, this fails. Before the loop, set j = '+' so a non-sign prefixed value is added (also, for style points, j is a terrible name, could you use op or something?).
You can't use variables as operators.
Replace:
result = result j i
with:
if j == '+':
result += i
else:
result -= i
Note: If modules are allowed, a generalization can be used to handle operators the "nice" way (though more work would be needed to obey operator precedence). You'd define:
import operator
ops = {'+': operator.add, '-': operator.sub, ...}
then make the initial value of op operator.add and change the test for operators to:
if i in ops:
op = ops[i]
else:
result = op(result, int(i))
which scales to many more operators, dynamically selecting the operation to perform without cascading if/elif checks.
Side-note: While violating the spirit of the challenge, ast.literal_eval (at least as of Python 3.5, and this may change, see bug #22525) will actually safely parse strings like this (eval is unsafe, since it can execute arbitrary code, but ast.literal_eval can only parse Python literals and apparently some basic compile-time math). So you could just do:
import ast
ans = ast.literal_eval
Sure, it handles many other literals too, but we never defined the failure case behavior anyway. :-)
Using eval() is the simplest solution. Like
eval("1+2-3")
The following code give another solution without using built-in eval
import operator
class Parse(object):
def __init__(self, input):
self.input = input
self.pos = 0
self.end = len(input)
def eval(self):
result = self.match_digits()
while self.pos < self.end:
op = self.match_operator()
operand = self.match_digits()
result = op(result, operand)
return result
def match_operator(self):
look_ahead = self.input[self.pos]
self.advance()
return operator.add if look_ahead == '+' else operator.sub
def match_digits(self):
look_ahead = self.input[self.pos]
positive = 1
if look_ahead == '-':
positive = -1
self.advance()
digits, s = 0, self.pos
while s < self.end and self.input[s].isdigit():
digits = digits * 10 + int(self.input[s])
s += 1
self.advance(s-self.pos)
return digits * positive
def advance(self, offset=1):
self.pos += offset
For testing
p = Parse(input='2+1+0-3')
print p.eval()
p = Parse(input='-2+-13+3')
print p.eval()
I think the most flexible solution (not using eval and able to handle any operations) is to parse the string into a binary (red-black) tree, where leafs are numbers and branches operators (+,-,/,*,etc).
For example, "1+(5*12)/17" would be parsed into following structure:
"+"
/ \
1 "/"
/ \
"()" 17
/
"*"
/ \
5 12
Once you've parsed a string into this structure, it's easy to compute by traversing branches depth-first, right to left.
If you need to handle variables, then you'd have to get locals() and replace accordingly, either as you parse the string, or as you traverse the tree.
EDIT:
I created a working example to illustrate this, you can find the source on github: https://github.com/MJWunderlich/py-math-expression-evaluator
what about:
def f(s):
s = s.strip()
for i, c in enumerate(s):
if c == '+':
return f(s[:i]) + f(s[i+1:])
if c == '-':
return f(s[:i]) - f(s[i+1:])
for i, c in enumerate(s):
if c == '*':
return f(s[:i]) * f(s[i+1:])
if c == '/':
return f(s[:i]) / f(s[i+1:])
return 0 if s == '' else int(s)
? Doesn't work with parenthesis