15-112 Lecture 5 (Wednesday, May 29, 2013)

A Quick Look at continue and break

A couple of minor features of the Python language that we might want to use from time-to-time are continue and break.

The example below illustrates continue, which causes control to immediately jump to the top of the loop, where the predicate is re-evaluated, without first running whatever code happens to be later in the loop.


def getUserSelection():
  VALID_OPTIONS = "ARPQ"
  MAX_ATTEMPTS = 3

  for attempt in range(3):
    # Print Menu
    print "--- Menu ---"
    print "A)dd a name"
    print "R)emove a name"
    print "P)rint all names"
    print "Q)uit"

    command = raw_input("command> ")

    # If the user supplies an empty line to raw_input
    # This raw_input() returns None
    # Just, FYI, None is equal to "", so we could test for that, too
    if (command == None):
      continue 

    if (command in VALID_OPTIONS):
      return command

print getUserSelection()
  

Not that, in the example below, break causes control flow to immediately leave the loop. It does not jump to the beginning, nor does it execute the code below -- it goes to the "bottom" of the loop, where execution would normally go once the loop finishes looping.


def addNumbers():
  # String containing legal number 
  NUMBERS = "0123456789"
  sum = 0

  while (True):
    number = raw_input("number> ")

    # This is really weak. We'll learn a better way, momentarily
    if ((len(number)!=1) or (number not in NUMBERS)):
      break     # Notice that we drop out of the loop here

    sum = sum+int(number)

  return sum

print addNumbers()
  

It is worth noting that code can get pretty tangled ocne you start making use of continue and break. The answer to the question, "Why am I looping?" becomes very hard to understand if the predicate isn't expressed in one place. And, the answer to the question, "What am I repeating?" becomes equally hard to understand if the answer is something different with each iteration. break often makes more of a mess than continue, because it undermines the loop's predicate as an invariant that describes the circumstances under which the loop is in effect. By contrast, continue complicates ones ability to understand which code will execute -- but doesn't undermine the loop's predicate, whcih is still tested before each repetition and the violation of which is still the exit condition for the loop.

If you find yourself using continue or break

Exceptions

Let's consider the following code. I know it doesn't work as written. But, what I'd like to do is to consider how it breaks:


def addNumbers():
  sum = 0

  while (True): 
    number = raw_input("number> ")
 
    sum = sum+int(number)

  return sum

print addNumbers()
  

It adds numbers, so long as it is fed integer numbers -- but then it dies when it is fed anything else:


Traceback (most recent call last):
  File "./example.py", line 13, in 
    print addNumbers()
  File "./example.py", line 9, in addNumbers
    sum = sum+int(number)
ValueError: invalid literal for int() with base 10: 'q'
  

Now, let's considered the revised version below. Any idea what it is doing?


def addNumbers():
  sum = 0

  while (True): 
    number = raw_input("number> ")
 
    try:
      sum = sum+int(number)
    except:
      break

  return sum

print addNumbers()
  

What do you think the try and except do? In effect, try tries to execute the associated block of code as normal, but if there is an error, looks to one or more associated except blocks for how to handle the error. In this case, we handle the error by breaking out of the loop.

So, now our function adds numbers provided by the usr, and lets the user stop by entering a non-number. Pretty cool, huh?

The Exception, as an Object

Exceptions are actually special types of complex entities called, "Objects". They contain data and associated functions, known as "methods".

When the user enters a non-number, and we try to use it as a number, Python generates an exception object to describe this situation. In effect, this is presented as part of the "except:" block. In the code above, we used a short-hand "except:" syntax that ignored this exception object.

Notice that, in the "except block" below, we use "as" to assign this object to the variable "e", and then print it. This is obviously not helpful to our user -- but it is a good example for us.


def addNumbers():
  sum = 0

  while (True): 
    number = raw_input("number> ")
 
    try:
      sum = sum+int(number)
    except:
      break

  return sum

print addNumbers()
  

The output of the "print e" above, is shown below:


invalid literal for int() with base 10: 'a'