Search code examples
pythoniteratorpython-itertools

Reading current iterator value without incrementing in Python


I am writing a program with two distinct states (a reversible unit converter between feet and meters) and several functions within the program depend on the current state (the current value of an alternating itertools.cycle() iterator). The user can call the reverse function to switch the current state and reverse the calculate conversion function.

Currently, I use next(currentstate) to return the next value of the iterator like this:

self.currentstate = cycle(range(2))    

def reverse(self):
    if next(self.currentstate) == 0:
        self.feet_label.grid(column=2, row=2, sticky=tk.W)
    if next(self.currentstate) == 1:
        self.feet_label.grid(column=2, row=1, sticky=tk.W)

def calculate(self, *args):
    if next(self.currentstate) == 0:
        # convert feet to meters
    if next(self.currentstate) == 1:
        # convert meters to feet

Unfortunately, whenever a function is called and the if statement is computed, the cycle iterator is incremented by the next operator and the next call will yield something different. The calculate function will likely be called many times in the same state, and so I'd like some way of retrieving the current value of the iterator without modifying or increment the operator.

def calculate(self, *args):
    if currentvalue(self.currentstate) == 0:
        # convert feet to meters
    if currentvalue(self.currentstate) == 1:
        # convert meters to feet

I have found a really ugly work-around involving calling next(currentvalue) twice in every if statement to reset the binary value. This may be a really awful way of writing a two state program like this, but it seems like there should be a way of doing this. I am very new to Python and may also not fully understand the underlying theory of iterators.

Thanks


Solution

  • It doesn't sound like you should be using an iterator here. You should use something that has to EXPLICITLY change state. It might be better to wrap this all in its own class.

    class StateMachine(object):
    
        STATE_ON = 1
        STATE_OFF = 0  # this could be an enum maybe?
    
        def __init__(self, starting_state=0):
            self.state = starting_state
    
        def self.change_state(self):
            if self.state = self.STATE_ON:
                self.state = self.STATE_OFF
            else:
                self.state = self.STATE_ON
    

    Now anywhere you use your state machine, you'll have to explicitly change the state.

    statemachine = StateMachine()
    
    def calculate(*args):
        if statemachine.state == statemachine.STATE_ON:
            do_something
        if statemachine.state == statemachine.STATE_OFF:
            do_something_else
    
    def switch_state(*args):
        do_something  # and...
        statemachine.change_state()