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()