1

I have a while loop that is reading user input. But I want the user to be able to break the while loop at any point, so they can go back to a main menu.

At the moment this is the only way I can do it (if statements after every point that the user may have typed '--back'. The menu.code is returned from the menu.parse function if it is changed. But then the while loop has to completely finish before it's new menu code takes effect. Is there a way of doing this without all the if statements?

menu is a class, and there are a couple of others. Below is a section from the main loop.

Some of the menu class:

class Menu:

    def __init__(self):
                self.code = 'main'
                self.codememory = []

    def exit(self, input):
        if input == '--exit':
            sys.exit()

    def back(self, input):
        if input == '--back':
            if 'main' in self.codememory:
                print "should be going back"
                print self.code
                self.code = self.codememory.pop()
                print self.code

    def move(self, input):
        if input == '--new':
            self.codememory.append(self.code)
            self.code = 'new'
        if input == '--main':
            self.codememory.append(self.code)
            self.code = 'main'
        if input == '--help':
            self.codememory.append(self.code)
            self.code = 'help'

    def select(self, input):
        if input in string.digits:
            input = int(input)
            if input == 1:
                self.codememory.append(self.code)
                self.code = 'new'
            if input == 2:
                self.codememory.append(self.code)
                self.code = 'test'

    def header(self, title):
        os.system('clear')
        print "-------------------"
        print title.upper()
        print "-------------------"
        print ""

    def parse(self, input):
        self.exit(input)
        self.back(input)
        if self.code == 'new':
            return input.lower().decode('utf-8')

############################
   menu = Menu()



        while 1:

            ...

            while menu.code == 'new':
                    menu.header('save word')
                    key = menu.parse(raw_input("Norwegain: "))
                    while key in dict.keys:
                        print "word already Saved in Dictionary"
                        key = menu.parse(raw_input("Norwegain: "))
                    if menu.code == 'new':
                        val = menu.parse(raw_input("English: "))
                    if menu.code == 'new':
                        syn = menu.parse(raw_input("Synonym: "))
                    if menu.code == 'new':
                        ant = menu.parse(raw_input("Antonym: "))

            while menu.code == 'main':
                    ...
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
beoliver
  • 5,579
  • 5
  • 36
  • 72
  • The code for your `menu` class would be great, because we wouldn't have to guess what it does, and we can give a solution we _know_ should work. – Edwin Mar 08 '12 at 22:30
  • i'll add it. Please be gentle... I'm learning! – beoliver Mar 08 '12 at 22:35
  • http://stackoverflow.com/questions/189645/how-to-break-out-of-multiple-loops-in-python – John Mar 08 '12 at 22:35
  • I don't understand why the "if menu.code == 'new':" is repeated. Is that a typo? Can menu.code change between the start of the while loop and the if statements? Can menu.parse change menu.code? – Travis Jensen Mar 08 '12 at 22:37
  • I don't understand why the "if menu.code == 'new':" is repeated. Is that a typo? Can menu.code change between the start of the while loop and the if statements? Can menu.parse change menu.code? If the answer to these is "Yes", I think you have a more significant structural problem that will make your code very hard to maintain. – Travis Jensen Mar 08 '12 at 22:38
  • I don't understand why the "if menu.code == 'new':" is repeated. Is that a typo? Can menu.code change between the start of the while loop and the if statements? Can menu.parse change menu.code? If the answer to these is "Yes", I think you have a more significant structural problem that will make your code very hard to maintain. – Travis Jensen Mar 08 '12 at 22:38
  • I don't understand why the "if menu.code == 'new':" is repeated. Is that a typo? Can menu.code change between the start of the while loop and the if statements? Can menu.parse change menu.code? If the answer to these is "Yes", I think you have a more significant structural problem that will make your code very hard to maintain. – Travis Jensen Mar 08 '12 at 22:39
  • hmm having problems editing... i'll have a look at the link posted first. – beoliver Mar 08 '12 at 22:41
  • EXACTLY... menu.parse() call menu.back() function. which can change the menu.code. How do I learn proper structure? – beoliver Mar 08 '12 at 23:03

3 Answers3

3

To break out of multiple loops in Python, wrap the loops in a function and return from it when you would like to leave:

while 1:
    def donew():
       while menu.code == 'new':
          blah ...
          while key in dict.keys:
             blah ...
             if some_condition: return
          blah ...
          val = menu.parse(raw_input("English: "))
    donew()
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
1

Raise an exception to exit the loop. This is a common enough trick in Python and the way you describe your problem makes me believe it is actually some kind of exception you are willing to manage.

Code may look like below:

class OutMainLoop(Exception):
    pass

while 1:
    try:
       while menu.code == 'new':
          blah ...
          while key in dict.keys:
             blah ...
             if some_condition:
                raise OutToMainLoop()
          blah ...
          val = menu.parse(raw_input("English: "))
    except OutToMainLoop:
        pass

But in this case it would certainly be even more natural to raise the exception from inside the menu class.

It very easy to do in Python. The Drawback is that is makes the code flow slightly harder to follow, but your example really looks like a legitimate use of an Exception.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
kriss
  • 23,497
  • 17
  • 97
  • 116
1

With this particular code, @kriss' suggestion to use exceptions is probably reasonable. Going a little more meta, you may want to look into state machines, though. It looks like your application is essentially a state machine, and, by using a state machine to handle transitions from one state to another, you could make your code much easier to follow and, more importantly, much easier to come back to in six months when somebody wants to add another command to it.

Travis Jensen
  • 5,362
  • 3
  • 36
  • 40