7

I am learning Python for the past few days and I have written this piece of code to evaluate a postfix expression.

postfix_expression = "34*34*+"

stack = []

for char in postfix_expression :
    try :
        char = int(char);
        stack.append(char);
    except ValueError:
        if char == '+' :
            stack.append(stack.pop() + stack.pop())
        elif char == '-' :
            stack.append(stack.pop() - stack.pop())
        elif char == '*' :
            stack.append(stack.pop() * stack.pop())
        elif char == '/' :
            stack.append(stack.pop() / stack.pop())

print stack.pop()

Is there a way I can avoid that huge if else block? As in, is there module that takes a mathematical operator in the string form and invokes the corresponding mathematical operator or some python idiom that makes this simple?

Abhi
  • 2,298
  • 4
  • 29
  • 34

2 Answers2

17

The operator module has functions that implement the standard arithmetic operators. With that, you can set up a mapping like:

OperatorFunctions = {
    '+': operator.add,
    '-': operator.sub,
    '*': operator.mul,
    '/': operator.div,
    # etc
}

Then your main loop can look something like this:

for char in postfix_expression:
    if char in OperatorFunctions:
        stack.append(OperatorFunctions[char](stack.pop(), stack.pop()))
    else:
        stack.append(char)

You will want to take care to ensure that the operands to subtraction and division are popped off the stack in the correct order.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • FYI - `operator` functions seem to be incredibly slow. `%timeit 1 + 3` - gives 12.5ns while `%timeit operator.add(1, 3)` gives 101ns (9 times slower :o) – AbdealiLoKo Jul 03 '18 at 09:41
  • @AbdealiJK 101 nanoseconds is not what I'd ordinarily call "incredibly slow". It's slowed down for a single test by the need to look up the function. This also isn't a fair test, because the `+` operator *isn't an object* and can't be called, passed to other functions as a higher-order function, etc. On my machine, `timeit.timeit("add(1,3)", setup="from operator import add")` is almost 4 times as fast as `timeit.timeit("(lambda x, y: x + y)(1,3)")`. – Karl Knechtel Oct 10 '22 at 23:33
  • Interresting - fair point @KarlKnechtel - depends on usage – AbdealiLoKo Oct 11 '22 at 13:28
-1
# This code is untested
from operator import add, sub, mul, div
# read the docs; this is a tiny part of the operator module

despatcher = {
    '+': add,
    '-': sub,
    # etc
    }

opfunc = despatcher[op_char]
operand2 = stack.pop()
stack[-1] = opfunc(stack[-1], operand2)
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
John Machin
  • 81,303
  • 11
  • 141
  • 189
  • @over-zealous editor: "despatcher" is an accepted (although allegedly less common) alternative to "dispatcher". I have rolled your edit back. Please leave it alone. – John Machin Jul 07 '09 at 08:17
  • @John: Is this one of those "British English" vs. "American English things? – PTBNL Jul 07 '09 at 18:18
  • @PTBNL: I don't know. FWIW: Google hit counts (millions, 3 sig. digits): despatch 8.25, dispatch 34.6, despatcher 8.42, dispatcher 7.98. Interesting inversion. – John Machin Jul 08 '09 at 02:52