pythonloopsstopiteration

StopIteration is being raised and I can't understand why (using next method)


I want my code to understand a string as a math expression, then output me this string with proper whitespaces.

  1. At first, I created my variables.
operators = ("+", "-", "*", "/", "%", "**", "//") # Math operators the code is going to identify in the string

oper = "20 *25" # The operation string before being formatted ('oper' stands for 'operation')
new_oper = "" # This is going to be the new operation string (desired output)

it = iter(oper.replace(" ", "")) # Removed all whitespaces from the operation string and made it an iterable
  1. I used the next() method once, just so he could understand this method (inside my loop) as the character that comes right after the character the loop is in.
next(it)
  1. Then I started my loop to check every single character in the operation string (already with no whitespaces).
for x in oper.replace(" ", ""):
    try: # This try checks if I'm able to use the next() method. If I'm not, x is the last character in the string.
        if x not in operators: # x is a number
            if next(it) in operators:
                new_oper += f"{x} " # If an operator comes right after a number, this number is printed with a whitespace, so they don't stay next to each other.
            else:
                new_oper += x   
        elif x in operators: # x is an operator
            if x == "*":
                if next(it) == "*":
                    new_oper += x # If another '*' comes after this '*', then it's a ** operator, so no whitespaces inserted (otherwise, they would be separated)
                else:
                    new_oper += f"{x} "
            elif x == "/":
                if next(it) == "/":
                    new_oper += x # Same here: if there are 2 '/' next to each other, it's another operator ('//'), so no whitespaces inserted
                else:
                    new_oper += f"{x} "
            else:
                new_oper += f"{x} "
    except RuntimeError:
        new_oper += x # x is the last character in the string, no needs to be formatted (it can't be an operator)
  1. Finally, after formatting everything, I wanted him to print the new string (new_oper).
print(new_oper)

Check out these outputs (different values for oper):

When

oper = "20 *2"

Output

Traceback (most recent call last):
  File "filename", line 12, in <module>
    if next(it) in operators:
       ^^^^^^^^
StopIteration

# Desired output: 20 * 2

When

oper = "20 +2"

Output

20 + 2 # Correct output (just changed the operator comparing to the last one)

When

oper = "10+ 3 *2"

Output

10 + 3*2
# Desired output: 10 + 3 * 2

I'd appreciate any approach for my problem. I'm not a python expert (far away from that), so I also would appreciate any suggestions or even fixes in my code that may not directly help my problem.


Solution

  • Firstly, you need to change the except RuntimeError: to except StopIteration: so that it is able to catch the StopIteration exception that is thrown by the next() when it reaches the end of the string.

    You need to change the else to make it like this:

    else:
        new_oper += f"{x} "
        next(it)
    

    Here, the next(it) is so that the iterator is always in line with the value of x. You call next(it) once on all other path of the if except this one.

    With these 2 changes, it works on the examples given.


    Suggestion:

    It might be easier to iterate over the string using enumerate like this:

    oper2 = oper.replace(" ", "")
    for index, x in enumerate(oper2):
    

    instead of trying to keep the iterator and the loop in sync. This way, you can refer to the next character as oper2[index+1] (index is the index of the current character) and for the current character, you can use x.
    For example, instead of next(it), you would write oper2[index+1]. This has the added benefit of being able to look further ahead (but you would need to do except IndexError: instead of except StopIteration:)