3: Getting Input

3: Getting Input

Book ref: Pg 60ff
Python 2.7: this function is called raw_input() in Python 2.7.

Pretty much any program you’re ever going to write will involve 3 parts – getting data, processing data and outputting a result. In your Hello world! program you learned one way of outputting data with the print() function. In 3: Python for Homework you learned how to process some data. But you still don’t have a way to get data into the program. That’s what this post is all about – input().

You use the input() function get string literals that the user types in at the keyboard. Try it now:

>>> input()
this was a blank line before I typed this
'this was a blank line before I typed this'

You need to type input() and hit enter to really understand what is happening here. When I typed enter, Python gave me a blank line. Then I typed “this was a blank line before I typed this” and hit enter again (you should type anything you like). Then it echoed (repeated) what I typed back to the screen.

If you put your own string literal inside the brackets the input() function will echo that literal before it gets input from the user. Here is an example:

>>> input("What is your name?")
What is your name?Brendan
'Brendan'

Instead of a blank line, input gave me a line starting with What is your name?. You can use this to tell the user what it is you want them to input. Notice also that there is no space between the question mark and the start of my answer. This is because Python has no idea about English grammar. You have to do that for Python. Remember to add a space to the end of your literals so that they look right:

>>> input("What is your name? ")
What is your name? Brendan 
'Brendan'

That space makes all the difference, don’t you think?

You can save the literals that someone types in by naming them – exactly how you saved literals in the earlier post. If you name the literal it is not echoed, but you can see it by printing the name you gave it. Here is an example:

>>> users_name = input("What is your name? ")
What is your name? Brendan 
>>> print(users_name)
Brendan 

Python thinks that everything that the user types is a string literal – so you can’t enter a number and expect to be able to add and multiply (etc) it:

>>> users_number = input("Please type in a number: ")
Please type in a number: 11
>>> users_number * 2
'1111'
>>> users_number + 2
Traceback (most recent call last):
  File "", line 1, in 
TypeError: Can't convert 'int' object to str implicitly

When you typed in 11, Python attempted to multiply it by 2 and got “1111” – that is, 11 repeated rather than 22. When you tried to add 2 to it, it failed completely. If you are expecting your user to enter a number you need to use another function, called int() to convert it to a ‘Python number’:

>>> int(users_number) * 2
22
>>> int(users_number) + 2
13

But notice, unless you rename it, the int() function does not change what’s stored (try users_number * 2 again). You can rename it by putting the name on both sides of the equal sign like this:

>>> users_number = int(users_number)
>>> users_number * 2
22

Now, Python is happy to treat this as an honest-to-goodness number.

If your user inputs a decimal number, int() won’t work. In that case you need to use a similar function, called float() instead:

>>> users_number = input("Please type in a decimal number: ")
Please type in a decimal number: 2.5
>>> int(users_number)
Traceback (most recent call last):
  File "", line 1, in 
ValueError: invalid literal for int() with base 10: '2.5'
>>> float(users_number)
2.5  

You can get the rough equivalent age of a dog in human years by multiplying it by 7 (purists will quibble that this is too inexact). For example, ie a 1 year old dog is roughly 7 human years old. Now write a short program to calculate the age of your dog in human years (notice here, I’m printing more than one thing by adding a comma between the things I’m printing):

>>> dog_age = input("How old is your dog? ")
How old is your dog? 2.5
>>> print("Your dog is about ", float(dog_age)*7, " years old.")
Your dog is about  17.5  years old.

Try it now!

3: Python for Homework

Book ref: Pg 64-66

If you are ever stuck for a calculator, Python can be a stand in for you. It is able to do any calculations that a calculator can do. Some things might be easier on a calculator, but, since Python is a general purpose programming language, there’s a heap of things that Python can calculate that your calculator can’t.
Addition and subtraction are pretty straight forward, just use the + and – keys on your keyboard like this:

>>> 1+1
2
>>> 2-1
1

Try some yourself.

In the last post you were using string literals. Here you’re using numbers, but you can use them in the same way that you can with string literals. For example, you can give them names (here, I’m using a and b as very simply names):

>>> a = 1
>>> b = 2

As in the last post, once you’ve named them you can use the names to refer to the numbers indirectly:

>>> a + b
3
>>> b - a 
1

Addition and subtraction are easy because you have a plus and minus sign on your keyboard. Not so multiplication or division! Since there’s no times sign on the keyboard, and you aren’t able to write a number on top of another for divide, Python instead uses symbols that are already on your keyboard, You use * for multiply and / for divide (on my keyboard * is Shift-8 and / is next to my right hand Shift key). Note that / starts in the bottom left and goes to the top right. You don’t want \ – that’s a different character. Here are some examples:

>>> 2*3
6
>>> 6/2
3.0

Try some yourself!

In the example above, see that the answer 6/2 gives a decimal answer (ie it ends in .0), even though 2*3 doesn’t. It used to in Python 2. In Python 2 you’d get this:

>>> 6/2
3

However, Python 2 also did this:

>>> 7/2
3

When you used / for division and both numbers were whole numbers, Python 2 would round the answer down to the next whole number. If you want to do this in Python 3 (stranger things have happened!) you use a double slash //. Like this:

>>> 7/2
3.5
>>> 7//2
3

The other thing you might be interested in trying is raising a number to a power. To do this in Python you use a double star: **. To calculate 3 squared and cubed respectively you would type:

>>> 3**2
9
>>> 3**3
27

To find the square, or cube root, you raise to the power of 0.5 and 1/3 respectively:

>>> 9**(0.5)
3.0
>>> 27**(1/3)
3.0

Python has the advantage that it can calculate stuff really quickly and display many more digits than your calculator can. If you want to know what 2 to the thousand is, Python can work it out in the blink of an eye:

>>> 2**1000
10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376L

Try some yourself. If your computer stops responding Ctrl-C should stop it. If not, close the window and restart Python.

3: A Literal Assignment

Book ref: page 39ff

In the previous post you learned about Python’s print feature*. In that post you used print  to display the text:

hello world!

You did that by putting single quotes/apostrophes/inverted commas (‘) around the text to be displayed:

'hello world!'

This thing (inside the single quotes) is called a literal. In fact, it’s a string literal.  You can create any string you like at the command line by typing it in with single quotes around it:

>>> ‘hello world!’
‘hello world!’

Try creating some of your own now.  If you try to type a string at the command line without the single quotes Python gets upset:

>>> hello world!
 File "<stdin>", line 1
 hello world!
 ^
SyntaxError: invalid syntax

This failed because there were no quotes around the string.

When you create a literal, Python stores it in memory. However, you can’t get to that literal, because you don’t know where Python has stored it. You can know where Python stores the literal by giving it a name. You do that by:

  1. Thinking of a name
  2. Using the = give the name to the literal.

Here’s an example:

>>> a_name = 'hello world!'
>>>

In this case, the name is a_name. You can choose any name you like, subject to some constraints (see page 42 of my book). The main things to mention are that names can’t have spaces in them, and can’t start with a number:

>>> a name = 'hello world!'
 File "<stdin>", line 1
 a name = 'hello world!'
 ^
SyntaxError: invalid syntax
>>> 1stname = 'hello world!'
  File "<stdin>", line 1
    1stname = 'hello world!'
          ^
SyntaxError: invalid syntax

In the first case, there’s a space after the a. In the second the name starts with a number. Remember to put a single quote at the start and at the end of the string.  Think up a name and assign it to ‘hello world!’ (or think up some other string!).

After you give a name to a literal then, whenever you use that name, it’s the same as retyping the literal:

>>> a_name = 'hello world!'
>>> a_name
'hello world!'
>>> print(a_name)
hello world!

Can you see that print(a_name) gives the same output as print(‘hello world!’)? That’s because thinks if Python is happy to let you Then putting that inside some brackets ():

('hello world!)

Then putting that on the right hand side of print:

print(‘hello world!’)

The text with the

* Actually, “function” is the technical term. You’ll learn about functions in a later post.

3: Hello World!

For those of you interested in learning Python 3, I’m going to start revisiting old blog posts, updating them for Python 3. They might, but probably won’t, be repeating the same order as the original. Python 3 posts will start with a 3 (in case you hadn’t worked out the 3: at the start of this posts’ title).  To do these posts I’m assuming that you’ve:

  1. Installed a version of Python starting with 3 (if you have a version starting with 2, start here).  If you haven’t installed it, follow the instructions on the Getting Started page.
  2. Managed to open Python (command line). You can also use the IDLE editor (and you’ll get onto that later anyways).

For some reason, lost to the mists of time, the first program you write in any language is supposed to be a program, called “Hello World”, that gets the computer to say “hello world”.    So here it is. Open up your Python prompt. You should see something like this:

>>>

That thing’s the Python prompt.  Type everything after the >>> in the first line here:

>>> print('hello world!')
hello world!
>>>

When you press the Enter* button at the end of the first line, “hello world!” appears on the next line.  Make sure that you type:

print('hello world!')

in exactly. That is,  (don’t type >>>) but make sure you include the (, ‘, ‘ and ).  If you leave any of them out, it won’t work (try it).

Congratulations, you’re now a programmer (in training).

 

*  If you are on a Mac, think “Return Button” whenever I say  Enter button.

 

Python for Kids: Bonus Project 1

In these posts I outline the contents of each project in my book Python For Kids For Dummies. If you have questions or comments about the project listed in the title post them here. Any improvements will also be listed here.

Bonus Projects!

Did you know that, in addition to the projects in my book, there are an addition 3 bonus projects available online? Hello GUI World, a Spline Drawing Program and Minecraft+Py+Pi. They were originally intended to be included in the book, but then they didn’t fit (which is a shame, since they were the book’s apotheosis). Happily for you though, they’re now online! – and there’s over 100 pages of extra material ! – it’s like you’re getting an extra third of the book! You can even do them even if you don’t have the book! It’s so exciting!! Follow the link in the sidebar to get them, even if you think I’ve overdone the exclamation points!!!

What’s in Bonus Project 1 (Hello GUI World!)

The body of the book lays the foundation for this project and Bonus Project 2. In this project you get to escape the shackles of the command line and create a graphical user environment (GUI) using the Tkinter widget toolkit that is included with Python.

This Project introduces the concept of a widget and introduces you to Label and Button widgets. You learn about callbacks. These are what you need to program in order for something to happen when you click the a button (for example). You learn how to change the configuration of widgets. This allows you, for example, to change the text that is displayed in the widget and the color of a widget.

Since you also need to know how to arrange different widgets in a window, the Project also covers the two main geometry managers (grid and pack). You’re shown how to open and close Tkinter applications and how to use the premade dialog boxes that come with Tkinter (tkMessageBox and tkFileDialog).

Improvements:
If you want to extend yourself, the accompanying cheatsheet contains a list of widgets, with some sample code needed to get the widgets working.
None atm.

Python for Kids: Python 3 Summary of Changes

While my Python 3 posts seemed to stretch for pages and pages with differences, there actually aren’t very many changes at all. Most of that space is taken up by the code outputs (which often had only minor changes) and unchanged code (that had to be there for context). In fact, while the book is about 300 pages long, just a handful of changes are needed to get the whole of the code in the book to run in Python 3. Those changes (in alphabetical order by topic) are below. Check them out if you’re having trouble with your other Python 2.7 code:

Links to the Python 3 updates for each of the Projects:

Project 2, Project 3, Project 4, Project 5, Project 6, Project 7, Project 8, Project 9, Project 10.

class

In Python 3, classes inherit from object automatically, so you don’t need (object) in the first line of the class definition. It’s not an error, but it is superfluous.

# Python 2.7
>>> class AddressEntry(object):
        """
        AddressEntry instances hold and manage details of a person
        """
        pass

# Python 3
>>> class AddressEntry: # note: no (object)
        """
        AddressEntry instances hold and manage details of a person
        """
        pass

Floating point division

Python 2 code in the book will work with Python 3. Some changes to floating point is now automatic in Python 3, so the code to change a number into floating point (eg float(2)) is unnecessary.

import cPickle as pickle

Python 3 uses cPickle by default, so replace import cPickle as pickle by just import pickle. If you try to import cPickle, you’ll get an error.

open

In Python 3 open() has the same syntax as in Python 2.7, but uses a different way to get data out of the file and into your hands. As a practical matter this means that some Python 2.7 code will sometimes cause problems when run in Python 3. If you run into such a problem (open code that works in Python 2.7 but fails in Python 3), the first thing to try is to add the binary modifier – you’ll need it when reading and writing pickle files for instance. So, instead of ‘r’ or ‘w’ for read and write use ‘rb’ or ‘wb’.

#Python2.7
>>> import pickle
>>> FILENAME = "p4k_test.pickle"
>>>  dummy_list = [x*2 for x in range(10)]
>>>  with open(FILENAME,'w') as file_object: #now dump it!
        pickle.dump(dummy_list,file_object)

>>> # open the raw file to look at what was written
>>> with open(FILENAME,'r') as file_object: # change w to r!!!
        print(file_object.read())

#Python3
>>> import pickle
>>> FILENAME = "p4k_test.pickle"
>>>  dummy_list = [x*2 for x in range(10)]
>>>  with open(FILENAME,'wb') as file_object: #### note: 'wb' not 'w'
        pickle.dump(dummy_list,file_object)

>>> # open the raw file to look at what was written
>>> with open(FILENAME,'rb') as file_object:  ##### note 'rb' not 'r'
        print(file_object.read())        

print

Print – mostly the same, since I used Python 3 print syntax in the book. There is an issue with the print continuation character (trailing comma). That needs to be replaced by an end parameter:

#Python 2.7 code:
>>> my_message = "Hello World!"
>>> while True:
...       print(my_message), #<- notice the comma at the end ... #Python 3 >>> my_message = 'Hello World!'
>>> while True:
...       print(my_message, end="")
...

If you’re using Python 2.7 code that’s not in my book, it might look like this:

#Python 2.7 code:
>>> print "Hello World"

To make this work in Python 3, you put brackets around what’s to be printed:

#Python 3
>>> print("Hello World")

raw_input v input

In Python 3 replace raw_input by input wherever you see it. Literally, input is simply a new name for raw_input.

Range vs xrange

The book uses range in anticipation of upgrading to Python 3, so mostly the code will work without changes! If you have code that uses xrange, just rename it to range and all should be well.

In one case the code assumed that the output of range is a list (which is is in Python 2.7, but not in Python 3). The code’s syntax was correct, but led to a logical error. That was corrected by choosing a way to test for the end of the loop that didn’t assume a list was involved.

Python for Kids: Python 3 – Project 10

Using Python 3 in Project 10 of Python For Kids For Dummies

In this post I talk about the changes that need to be made to the code of Project 10 of my book Python for Kids for Dummies in order for it to work with Python 3. The main difference between the Python 2.7 and Python 3 code for this project is that Python 3 uses raw_input and that has been renamed to input in Python 3. Most of the code in project 10 will work with this one change. However, in a lot of cases what Python outputs in Python 3 is different from the output in Python 2.7. This project has a lot of code. In order to shorten the length of this post I am only showing the Python 3 versions of the longer pieces (rather than both Python 2.7 (from the book) and Python 3). Look at the book to see the Python 2.7 code (it’s very similar).

Disclaimer

Some people want to use my book Python for Kids for Dummies to learn Python 3. I am working through the code in the existing book, highlighting changes from Python 2 to Python 3 and providing code that will work in Python 3. If you are using Python 2.7 you can ignore this post. This post is only for people who want to take the code in my book Python for Kids for Dummies and run it in Python 3.

######## Page 283

The code on this page uses raw_input, which has been renamed to input in Python 3. You can either replace all occurrences of raw_input with input or add a line:

raw_input = input 

at the start of the relevant code. In order to reduce the amount of code being repeated, I am adding raw_input = input to the Constants section of the code. You will need to remember that all of the later code assumes that this line has been added.

  
"""
Python 2.7
math_trainer.py
Train your times tables.
Initial Features:
* Print out times table for a given number.
* Limit tables to a lower number (default is 1)
and an upper number (default is 12).
* Pose test questions to the user
* Check whether the user is right or wrong
* Track the user's score.
Brendan Scott
February 2015
"""
#### Constants Section
TEST_QUESTION = (4, 6)
QUESTION_TEMPLATE = "What is %sx%s? "
#### Function Section
#### Testing Section
question = TEST_QUESTION
prompt = QUESTION_TEMPLATE%question
correct_answer = question[0]*question[1] # indexes start from 0
answer = raw_input(prompt)
if int(answer)== correct_answer:
    print("Correct!")
else:
    print("Incorrect")

>>> ================================ RESTART ================================
>>>
What is 4x6? 24
Correct!
>>> ================================ RESTART ================================
>>>
What is 4x6? 25
Incorrect

     

"""
Python 3
math_trainer.py
Train your times tables.
Initial Features:
* Print out times table for a given number.
* Limit tables to a lower number (default is 1)
and an upper number (default is 12).
* Pose test questions to the user
* Check whether the user is right or wrong
* Track the user's score.
Brendan Scott
February 2015
"""
#### Constants Section
raw_input = input # this line added
TEST_QUESTION = (4, 6)
QUESTION_TEMPLATE = "What is %sx%s? "

#### Function Section

#### Testing Section
question = TEST_QUESTION
prompt = QUESTION_TEMPLATE%question
correct_answer = question[0]*question[1] # indexes start from 0
answer = raw_input(prompt)
if int(answer)== correct_answer:
    print("Correct!")
else:
    print("Incorrect")

>>> ================================ RESTART ================================
>>>
What is 4x6? 24
Correct!
>>> ================================ RESTART ================================
>>>
What is 4x6? 25
Incorrect

######## Page 286-296

All code on this page is the same, and all outputs from the code is the same in Python 3 as in Python 2.7.
Remember that for the code to work in Python 3 code an additional line

raw_input = input

as added in the Constants section of the code.

######## Page 297

All code on this page is the same, and all outputs from the code is the same in Python 3 as in Python 2.7.

######## Page 298
The code in this section is different in Python 2.7 v Python 3.
The Python 2.7 code assumed that there was a list and that a while loop repeatedly removed things from that list. When everything was removed then the loop stopped. This was achieved by a test

batch != []

that is, stop when the variable batch is an empty list.
Ultimately, what is in batch comes from a call to the range builtin:

tables_to_print = range(1, upper+1)

In Python 2.7 this is a list which is generated in full and stored in tables_to_print. In Python 3 it’s not. Rather the range builtin generates the values that are needed at the time they are needed – not before. In Python 3 batch is a “range object”, not a list. And, while batch gets shorter and shorter, it’s never going to be an empty list (it would need to stop being a range and start being a list), no matter how long the program runs. To get this code working in Python 2.7 you can either:
(A) explicitly make batch a list by changing the line:

tables_to_print = range(1, upper+1)

to

tables_to_print = list(range(1, upper+1))

this changes all the relevant variables (and, in particular batch) into lists so the condition in the while loop will evaluate as you expect; or

(B) change the condition in the while loop to check the length of batch rather than whether or not it is an empty list. That is change:

 
    while batch != []: # stop when there's no more to print

to

 
    while len(batch) > 0: # stop when there's no more to print

That is, once the length is 0 (ie no more elements to display), stop the loop. I think this is the better of the two options because it makes the test independent of the type of variable used to keep track of batches.

Remember that the Python 3 code has an additional line

 
raw_input = input

in the Constants section of the code.

 
#Python 2.7

TIMES_TABLE_ENTRY = "%2i x %2i = %3i "

def display_times_tables(upper=UPPER):
    """
    Display the times tables up to UPPER
    """
    tables_per_line = 5
    tables_to_print = range(1, upper+1)
    # get a batch of 5 to print
    batch = tables_to_print[:tables_per_line]
    # remove them from the list
    tables_to_print = tables_to_print[tables_per_line:]
    while batch != []: # stop when there's no more to print
        for x in range(1, upper+1):
            # this goes from 1 to 12 and is the rows
            accumulator = []
            for y in batch:
                # this covers only the tables in the batch
                # it builds the columns
                accumulator.append(TIMES_TABLE_ENTRY%(y, x, x*y))
            print("".join(accumulator)) # print one row
        print("\n") # vertical separation between blocks of tables.
        # now get another batch and repeat.
        batch = tables_to_print[:tables_per_line]
        tables_to_print = tables_to_print[tables_per_line:]

     

#Python 3                            
TIMES_TABLE_ENTRY = "%2i x %2i = %3i "

def display_times_tables(upper=UPPER):
    """
    Display the times tables up to UPPER
    """
    tables_per_line = 5
    tables_to_print = list(range(1, upper+1))
    # get a batch of 5 to print
    batch = tables_to_print[:tables_per_line]
    # remove them from the list
    tables_to_print = tables_to_print[tables_per_line:]
    while len(batch)>0: # stop when there's no more to print
        for x in range(1, upper+1):
            # this goes from 1 to 12 and is the rows
            accumulator = []
            for y in batch:
                # this covers only the tables in the batch
                # it builds the columns
                accumulator.append(TIMES_TABLE_ENTRY%(y, x, x*y))
            print("".join(accumulator)) # print one row
        print("\n") # vertical separation between blocks of tables.
        # now get another batch and repeat.
        batch = tables_to_print[:tables_per_line]
        tables_to_print = tables_to_print[tables_per_line:]
        

######## Page 302, 304
All code on this page is the same, and all outputs from the code is the same in Python 3 as in Python 2.7.
Remember that the Python 3 code has an additional line

   
raw_input = input 

in the Constants section of the code.

######## Page 305-306

All code on this page is the same, and all outputs from the code is the same in Python 3 as in Python 2.7.

######## Page 307
All code on this page is the same, and all outputs from the code is the same in Python 3 as in Python 2.7.
Remember that the Python 3 code has an additional line

   
raw_input = input

in the Constants section of the code.

#########################################
### Full Code:
#########################################

The code in this section is different in Python 2.7 v Python 3.
The Python 3 code has an additional line
raw_input = input
in the Constants section of the code and the line

   
    while batch != []: # stop when there's no more to print

has been changed to

   
    while len(batch) > 0: # stop when there's no more to print
     
"""
math_trainer.py
Train your times tables.
Initial Features:
* Print out times table for a given number.
* Limit tables to a lower number (default is 1) and
an upper number (default is 12).
* Pose test questions to the user
* Check whether the user is right or wrong
* Track the user's score.
Brendan Scott
February 2015
"""

#### Imports Section
import random
import sys
import time

#### Constants Section
TEST_QUESTION = (4, 6)
QUESTION_TEMPLATE = "What is %sx%s? "
LOWER = 1
UPPER = 12
MAX_QUESTIONS = 10 # for testing, you can increase it later
TIMES_TABLE_ENTRY = "%2i x %2i = %3i "

INSTRUCTIONS = """Welcome to Math Trainer
This application will train you on your times tables.
It can either print one or more of the tables for you
so that you can revise (training) or you it can test
you on your times tables.
"""
CONFIRM_QUIT_MESSAGE = 'Are you sure you want to quit (Y/n)? '
SCORE_TEMPLATE = "You scored %s (%i%%) in %.1f seconds"

#### Function Section
def make_question_list(lower=LOWER, upper=UPPER, random_order=True):
    """ prepare a list of questions in the form (x,y)
    where x and y are in the range from LOWER to UPPER inclusive
    If random_order is true, rearrange the questions in a random
    order
    """
    spam = [(x+1, y+1) for x in range(lower-1, upper)
                       for y in range(lower-1, upper)]
    if random_order:
        random.shuffle(spam)
    return spam

def display_times_tables(upper=UPPER):
    """
    Display the times tables up to UPPER
    """
    tables_per_line = 5
    tables_to_print = range(1, upper+1)
    # get a batch of 5 to print
    batch = tables_to_print[:tables_per_line]
    # remove them from the list 
    tables_to_print = tables_to_print[tables_per_line:]
    while batch != []: # stop when there's no more to print
        for x in range(1, upper+1):
            # this goes from 1 to 12 and is the rows 
            accumulator = []
            for y in batch:
                # this covers only the tables in the batch
                # it builds the columns
                accumulator.append(TIMES_TABLE_ENTRY%(y, x, x*y))
            print("".join(accumulator)) # print one row
        print("\n") # vertical separation between blocks of tables.
        # now get another batch and repeat. 
        batch = tables_to_print[:tables_per_line]
        tables_to_print = tables_to_print[tables_per_line:]

    
def do_testing():
    """ conduct a round of testing """
    question_list = make_question_list()
    score = 0
    start_time = time.time()
    for i, question in enumerate(question_list):
        if i >= MAX_QUESTIONS:
            break
        prompt = QUESTION_TEMPLATE%question
        correct_answer = question[0]*question[1]
        # indexes start from 0
        answer = raw_input(prompt)

        if int(answer) == correct_answer:
            print("Correct!")
            score = score+1
        else:
            print("Incorrect, should have "+\
                  "been %s"%(correct_answer))

    end_time = time.time()
    time_taken = end_time-start_time
    percent_correct = int(score/float(MAX_QUESTIONS)*100)
    print(SCORE_TEMPLATE%(score, percent_correct, time_taken))

def do_quit():
    """ quit the application"""
    if confirm_quit():
        sys.exit()
    print("In quit (not quitting, returning)")

def confirm_quit():
    """Ask user to confirm that they want to quit
    default to yes 
    Return True (yes, quit) or False (no, don't quit) """
    spam = raw_input(CONFIRM_QUIT_MESSAGE)
    if spam == 'n':
        return False
    else:
        return True    


#### Testing Section

#do_testing()
##display_times_tables()

#### Main Section

if __name__ == "__main__":
    while True:
        print(INSTRUCTIONS)
        raw_input_prompt = "Press: 1 for training,"+\
                           " 2 for testing, 3 to quit.\n"
        selection = raw_input(raw_input_prompt)
        selection = selection.strip()
        while selection not in ["1", "2", "3"]:
            selection = raw_input("Please type either 1, 2, or 3: ")
            selection = selection.strip()

        if selection == "1":
            display_times_tables()
        elif selection == "2":
            do_testing()
        else:  # has to be 1, 2 or 3 so must be 3 (quit)
            do_quit()

     

"""
math_trainer.py (Python 3)
Train your times tables.
Initial Features:
* Print out times table for a given number.
* Limit tables to a lower number (default is 1) and
an upper number (default is 12).
* Pose test questions to the user
* Check whether the user is right or wrong
* Track the user's score.
Brendan Scott
February 2015
"""

#### Imports Section
import random
import sys
import time

#### Constants Section
raw_input = input
TEST_QUESTION = (4, 6)
QUESTION_TEMPLATE = "What is %sx%s? "
LOWER = 1
UPPER = 12
MAX_QUESTIONS = 10 # for testing, you can increase it later
TIMES_TABLE_ENTRY = "%2i x %2i = %3i "

INSTRUCTIONS = """Welcome to Math Trainer
This application will train you on your times tables.
It can either print one or more of the tables for you
so that you can revise (training) or you it can test
you on your times tables.
"""
CONFIRM_QUIT_MESSAGE = 'Are you sure you want to quit (Y/n)? '
SCORE_TEMPLATE = "You scored %s (%i%%) in %.1f seconds"

#### Function Section
def make_question_list(lower=LOWER, upper=UPPER, random_order=True):
    """ prepare a list of questions in the form (x,y)
    where x and y are in the range from LOWER to UPPER inclusive
    If random_order is true, rearrange the questions in a random
    order
    """
    spam = [(x+1, y+1) for x in range(lower-1, upper)
                       for y in range(lower-1, upper)]
    if random_order:
        random.shuffle(spam)
    return spam

def display_times_tables(upper=UPPER):
    """
    Display the times tables up to UPPER
    """
    tables_per_line = 5
    tables_to_print = range(1, upper+1)
    # get a batch of 5 to print
    batch = tables_to_print[:tables_per_line]
    # remove them from the list 
    tables_to_print = tables_to_print[tables_per_line:]
    while len(batch) > 0: # stop when there's no more to print
        for x in range(1, upper+1):
            # this goes from 1 to 12 and is the rows 
            accumulator = []
            for y in batch:
                # this covers only the tables in the batch
                # it builds the columns
                accumulator.append(TIMES_TABLE_ENTRY%(y, x, x*y))
            print("".join(accumulator)) # print one row
        print("\n") # vertical separation between blocks of tables.
        # now get another batch and repeat. 
        batch = tables_to_print[:tables_per_line]
        tables_to_print = tables_to_print[tables_per_line:]

    
def do_testing():
    """ conduct a round of testing """
    question_list = make_question_list()
    score = 0
    start_time = time.time()
    for i, question in enumerate(question_list):
        if i >= MAX_QUESTIONS:
            break
        prompt = QUESTION_TEMPLATE%question
        correct_answer = question[0]*question[1]
        # indexes start from 0
        answer = raw_input(prompt)

        if int(answer) == correct_answer:
            print("Correct!")
            score = score+1
        else:
            print("Incorrect, should have "+\
                  "been %s"%(correct_answer))

    end_time = time.time()
    time_taken = end_time-start_time
    percent_correct = int(score/float(MAX_QUESTIONS)*100)
    print(SCORE_TEMPLATE%(score, percent_correct, time_taken))

def do_quit():
    """ quit the application"""
    if confirm_quit():
        sys.exit()
    print("In quit (not quitting, returning)")

def confirm_quit():
    """Ask user to confirm that they want to quit
    default to yes 
    Return True (yes, quit) or False (no, don't quit) """
    spam = raw_input(CONFIRM_QUIT_MESSAGE)
    if spam == 'n':
        return False
    else:
        return True    


#### Testing Section

#do_testing()
##display_times_tables()

#### Main Section

if __name__ == "__main__":
    while True:
        print(INSTRUCTIONS)
        raw_input_prompt = "Press: 1 for training,"+\
                           " 2 for testing, 3 to quit.\n"
        selection = raw_input(raw_input_prompt)
        selection = selection.strip()
        while selection not in ["1", "2", "3"]:
            selection = raw_input("Please type either 1, 2, or 3: ")
            selection = selection.strip()

        if selection == "1":
            display_times_tables()
        elif selection == "2":
            do_testing()
        else:  # has to be 1, 2 or 3 so must be 3 (quit)
            do_quit()