Python: Change index inside a for loop

Question:

I’m writing a simple parser that reads a file. I’m reading the file contents into a list. I iterate through the list and send each line as an instruction to an instrument.

I’m iterating through the list like this:

for i, line in enumerate(mylines):

I check for keywords in the file and do some simple things like:

Delay N: Sleeps for N in increments one second 
Pause: Display a "Click to Continue" window

These work and of course were simple.

I’d like add a "Loop N …. EndLoop" where it will see the "Loop N" line, set a counter to N and make note of the index position of the next line (loop_start= i+ 1) of the for beginning of the loop.

Then keep iterating until it comes to the EndLoop, where I want to set i back to the noted beginning of the loop and decrement the counter.

How do can I reset the for loop index to get it to jump back to the beginning of the loop? Doesn’t seem to be as simple as:

i = loop_start

or is it?

Here’s my code:

    def interate(self, mylines):
    count = 0
    level = logging.INFO
    for i, line in enumerate(mylines):
       time.sleep(0.01)
       print(line)
       print(i)
       line = self.stripComments(line)
       print(line)
       count += 1
       loop_started = 0
       loop_count = 0
       if not line or re.search("^s*$", line):
            #print('String is either empty or Blank or contain only spaces')
            pass
       else:
            first_word = line.split()[0]

            if(first_word == "Delay"):
                second_word = line.split()[1]
                if second_word.isnumeric() and int(second_word) > 0:   
                    logger.log(logging.WARNING, "DelayingI " + second_word)
                    for i in range(int(second_word), 0, -1): 
                        time.sleep(int(1))
                        logger.log(logging.WARNING, "... " + str(i))
                elif self.is_float(second_word) and float(second_word)> 0: 
                    
                    intgr, dec = divmod( float(second_word), 1)
                    print("intgr: " + str(intgr))
                    logger.log(logging.WARNING, "DelayingF " + second_word)
                    
                    for i in range(int(intgr), 0, -1): 
                        time.sleep(int(1))
                        logger.log(logging.WARNING, "... " + str(i))
                    logger.log(logging.WARNING, "and " + str(dec) + "mS")
                    time.sleep(dec)
                   
                else:    
                    logger.log(logging.ERROR, "Bad Number" + second_word)
                    print("Bad number")  
            elif(first_word == "Pause"):
                top= tk.Toplevel(self.frame)
                x = self.frame.winfo_x()
                y = self.frame.winfo_y()
                top.geometry("+%d+%d" % (x + 100, y + 200))
                top.geometry("100x100")
                top.configure(bg='#6699cc')
                top.title("Child Window")
                pause_var = tk.IntVar()
                pause_button = tk.Button(top, text='Click to Continue', command=lambda: pause_var.set(1))
                pause_button.place(relx=.5, rely=.5, anchor="c")
                pause_button.wait_variable(pause_var)
                top.destroy()
            elif(first_word == "Loop"):
                if loop_started == 0:
                    print("loop started at " + (str(i)))
                    loop_started = 1
                    second_word = line.split()[1]
                    if second_word.isnumeric() and int(second_word) > 0:
                        begin_loop = i+i
                        num_loops = second_word;
                        print("number of loops: " + str(num_loops))
            elif(first_word == "EndLoop"):
                    print("loop end at" + (str(i)))
                    loop_count = loop_count+1
                    print("loop cout:" + (str(i)))
                    if loop_count != num_loops:                           
                        i=begin_loop
                    else:
                        loop_started = 0
                        loop_count = 0
                        
                
            else:
                serialPort.write(bytes(line + 'n', encoding='utf-8'))

Thanks!

Asked By: zonedar

||

Answers:

enumerate is managing the index here and it always goes to the next one. it doesn’t "add 1 to i" such that decreasing i will move it backward, it produces the next index (from an internal counter) and the for loop assigns that to i before beginning the iteration.

If you want to be able to change the index during iteration, the traditional way is to manage the index yourself and use a while loop instead of for. However, an alternative is to write your own enumerator that allows the index to be adjusted. For example:

class ListEnumerator:

    def __init__(self, seq, start=0):
        self.seq = seq
        self.index = start - 1

    def __iter__(self):
        return self

    def __next__(self):
        self.index += 1
        if self.index < len(self.seq):
            return self.index, self.seq[self.index]
        else:
            raise StopIteration

    # go back the given number of elements
    def back(self, off):
        self.index -= (off + 1)

    # skip ahead the given number of elements
    def skip(self, off):
        self.index += off

    # process the same item again on next iteration
    def again(self):
        self.index -= 1

    # process the previous item on next iteration
    def prev(self):
        self.index -= 2

    # process the given item on next iteration
    def set(self, index):
        self.index = index - 1

Usage:

items = [3, 1, 4, 1, 5, 2, 7, 2, 9]

for i, x in (e := ListEnumerator(items)):
    print(i, x)
    if x == 1:
        items[i] = 0
        e.prev()
Answered By: kindall
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.