Search code examples
pythonstack

Python Unexpected ZeroDivisionError for unsued dictionary operation


from collections import deque
def postfixExpression(expression: str) -> int:
    if not expression:
        return -1
    expression_list = expression.split(" ")
    stack = deque()
    for ex in expression_list:
        print(ex, stack)
        try:
            stack.append(int(ex))
        except ValueError:
            b = stack.pop()
            a = stack.pop()
            switcher = {
                "+" : a + b,
                "-" : a - b,
                "*" : a * b,
                "/" : int(a / b),
                "^" : a ** b,
            }
            if ex not in switcher.keys():
                raise ValueError(f"Wrong expression-> {ex}")
            else:
                stack.append(switcher.get(ex))
    return stack.pop()

expression = "2 3 7 - 7 + 9 7 2 + - * *"
print(f"\nResult {postfixExpression(expression)}")

Above is a Postfix Evaluation program in python. It's working for most case.

But when I used input "2 3 7 - 7 + 9 7 2 + - * *", it's giving ZeroDivisionError, even thought I didn't use divide(/) in input.

Error message:

$ python temp/temp.py 
4 deque([])
6 deque([4])
* deque([4, 6])
2 deque([24])
1 deque([24, 2])
- deque([24, 2, 1])
+ deque([24, 1])
7 deque([25])
7 deque([25, 7])
+ deque([25, 7, 7])
7 deque([25, 14])
6 deque([25, 14, 7])
+ deque([25, 14, 7, 6])
* deque([25, 14, 13])
* deque([25, 182])
3 deque([4550])
7 deque([4550, 3])
- deque([4550, 3, 7])
7 deque([4550, -4])
+ deque([4550, -4, 7])
9 deque([4550, 3])
7 deque([4550, 3, 9])
2 deque([4550, 3, 9, 7])
+ deque([4550, 3, 9, 7, 2])
- deque([4550, 3, 9, 9])
* deque([4550, 3, 0])
Traceback (most recent call last):
  File "C:\Users\...\temp.py", line 11, in postfixExpression
    stack.append(int(ex))
ValueError: invalid literal for int() with base 10: '*'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\...\temp.py", line 52, in main
    print(f"\nresult {postfixExpression(expression)}")
  File "C:\Users\...\temp.py", line 19, in postfixExpression
    "/" : int(a / b),
ZeroDivisionError: division by zero

When I comment "/" : int(a / b), line in switcher dict it's giving expected answer i.e 0.

Can anyone explain why I am getting this error (main) and how to solve it?


Solution

  • The problem is that you are evaluating all of the operations at the time you set up your switcher dictionary. So regardless of which operator your code encounters, it calculates the sum, difference, product, and quotient of the two values at the top of the stack. Then it looks at the operator to decide which one to actually use. In this case, your b value is zero, so it is indeed trying to divide by zero.

    There are a number of ways that you could fix this, including (but not limited to):

    • use lambda functions in your switcher dictionary, and only call the one that is needed
                switcher = {
                    "+" : lambda: a + b,
                    "-" : lambda: a - b,
                    "*" : lambda: a * b,
                    "/" : lambda: int(a / b),
                    "^" : lambda: a ** b,
                }
                if ex not in switcher:
                    raise ValueError(f"Wrong expression-> {ex}")
                else:
                    stack.append(switcher[ex]())  # <- note the added () here
    
    • just use a match statement to calculate the appropriate value directly in the code rather than using a dictionary (this requires Python 3.10+)
        match ex:
            case "+":
                val = a + b
            case "-":
                val = a - b
            case "*":
                val = a * b
            case "/":
                val = int(a / b)
            case "^":
                val = a ** b
            case _:
                raise ValueError(f"Wrong expression-> {ex}")
        stack.append(val)
    
    • use an if-elif chain to calculate the appropriate value directly in the code rather than using a dictionary
        if ex == "+":
            val = a + b
        elif ex == "-":
            val = a - b
        elif ex == "*":
            val = a * b
        elif ex == "/":
            val = int(a / b)
        elif ex == "^":
            val = a ** b
        else:
            raise ValueError(f"Wrong expression-> {ex}")
        stack.append(val)
    

    In any case, I would recommend that you pull the calculation operation into a function of its own instead of having it embedded in the middle of your loop. Something like

    def calculate_value(op, a, b):
        ...  # Something based on what I've shown above
    
    
    def postfixExpression(expression: str) -> int:
        if not expression:
            return -1
        expression_list = expression.split(" ")
        stack = deque()
        for ex in expression_list:
            print(ex, stack)
            try:
                stack.append(int(ex))
            except ValueError:
                b = stack.pop()
                a = stack.pop()
                stack.append(calculate_value(ex, a, b))
        return stack.pop()
    


    On request: the tested code using lambda functions:

    from collections import deque
    def postfixExpression(expression: str) -> int:
        if not expression:
            return -1
        expression_list = expression.split(" ")
        stack = deque()
        for ex in expression_list:
            print(ex, stack)
            try:
                stack.append(int(ex))
            except ValueError:
                b = stack.pop()
                a = stack.pop()
                switcher = {
                    "+" : lambda: a + b,
                    "-" : lambda: a - b,
                    "*" : lambda: a * b,
                    "/" : lambda: int(a / b),
                    "^" : lambda: a ** b,
                }
                if ex not in switcher.keys():
                    raise ValueError(f"Wrong expression-> {ex}")
                else:
                    stack.append(switcher.get(ex)())
        return stack.pop()
    
    expression = "2 3 7 - 7 + 9 7 2 + - * *"
    print(f"\nResult {postfixExpression(expression)}")
    

    with output

    2 deque([])
    3 deque([2])
    7 deque([2, 3])
    - deque([2, 3, 7])
    7 deque([2, -4])
    + deque([2, -4, 7])
    9 deque([2, 3])
    7 deque([2, 3, 9])
    2 deque([2, 3, 9, 7])
    + deque([2, 3, 9, 7, 2])
    - deque([2, 3, 9, 9])
    * deque([2, 3, 0])
    * deque([2, 0])
    
    Result 0