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

Python for Kids: Python 3 – Project 9

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

In this post I talk about the changes that need to be made to the code of
Project 9 in order for it to work with Python 3. Most of the code in project 9 will work without changes.
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. 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.

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 240
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.
However, in Python 3 you don’t need to add “(object)” when defining a class – it is assumed.
It is not an error to add (object) in Python 3, it just doesn’t look as nice. For example, on this page a class is defined
using class AddressEntry(object): (in Python 2.7). In Python 3 it’s just class AddressEntry:

    

>>> class AddressEntry(object):
        """
        AddressEntry instances hold and manage details of a person
        """
        pass
        
       
>>> AddressEntry # without parentheses
<class '__main__.AddressEntry'>
>>>
                            
                            
>>> AddressEntry() # parentheses create an instance
<__main__.AddressEntry object at 0x7f9309751590>


>>> address_entry = AddressEntry()
                         
                         
# Python 3 
>>> class AddressEntry: # note: no (object)
        """
        AddressEntry instances hold and manage details of a person
        """
        pass
        
       
>>> AddressEntry # without parentheses
<class '__main__.AddressEntry'>
>>>
                            
                            
>>> AddressEntry() # parentheses create an instance
<__main__.AddressEntry object at 0x7f9309751590>


>>> address_entry = AddressEntry()

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

Page 242
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.
You still need to use () when creating instances of an object. That is, making an instance of a class, rather than
defining the class.

Page 244
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
However, in Python 3 you don’t need to add “(object)” when defining a class – it is assumed.
It is not an error to add (object) in Python 3, it just doesn’t look as nice.

    
Python 2.7
"""
Addressbook.py
An address book program to store details of people I know.
Stuff I'm storing is:
first name
family name
email address
date of birth
[other stuff]
Brendan Scott
Feb 2015
"""
##### Classes Section
class AddressBook(object):
    """
    AddressBook instances hold and manage a list of people
    """
    pass
    
    
class AddressEntry(object):
    """
    AddressEntry instances hold and manage details of a person
    """
    
    
##### Main Section
if __name__ == "__main__":
    address_book = AddressBook()
    person1 = AddressEntry()

    
# Python3
"""
Addressbook.py
An address book program to store details of people I know.
Stuff I'm storing is:
first name
family name
email address
date of birth
[other stuff]
Brendan Scott
Feb 2015
"""
##### Classes Section
class AddressBook:
    """
    AddressBook instances hold and manage a list of people
    """
    pass
    
    
class AddressEntry:
    """
    AddressEntry instances hold and manage details of a person
    """
    
    
##### Main Section
if __name__ == "__main__":
    address_book = AddressBook()
    person1 = AddressEntry()
    

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

Page 246
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.
However, in Python 3 you don’t need to add “(object)” when defining a class – it is assumed.
It is not an error to add (object) in Python 3, it just doesn’t look as nice.

    
#Python2.7
class AddressEntry(object):
    """
    AddressEntry instances hold and manage details of a person
    """
    def __init__(self, first_name=None, family_name=None,
                  email_address=None, date_of_birth=None):
        """Initialize attributes first_name,
        family_name and date_of_birth.
        Each argument should be a string.
        date_of_birth should be of the form "MM DD, YYYY"
        """
        self.first_name = first_name
        self.family_name = family_name
        self.email_address = email_address
        self.date_of_birth = date_of_birth
        
        
#Python3
class AddressEntry:
    """
    AddressEntry instances hold and manage details of a person
    """
    def __init__(self, first_name=None, family_name=None,
                  email_address=None, date_of_birth=None):
        """Initialize attributes first_name,
        family_name and date_of_birth.
        Each argument should be a string.
        date_of_birth should be of the form "MM DD, YYYY"
        """
        self.first_name = first_name
        self.family_name = family_name
        self.email_address = email_address
        self.date_of_birth = date_of_birth
        

Page 248
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.
However, in Python 3 you don’t need to add “(object)” when defining a class – it is assumed.
It is not an error to add (object) in Python 3, it just doesn’t look as nice.

    
############Python 2.7
##### Classes Section
class AddressBook(object):
    """
    AddressBook instances hold and manage a list of people
    """
    pass
    
    
class AddressEntry(object):
    """
    AddressEntry instances hold and manage details of a person
    """
    def __init__(self, first_name=None, family_name=None,
                 email_address=None, date_of_birth=None):
          """Initialize attributes first_name,
          family_name and date_of_birth.
          Each argument should be a string.
          date_of_birth should be of the form "MM DD, YYYY"
          """
          self.first_name = first_name
          self.family_name = family_name
          self.email_address = email_address
          self.date_of_birth = date_of_birth
          
          
##### Main Section

if __name__ == "__main__":
    address_book = AddressBook()
    person1 = AddressEntry("Eric", "Idle", None, "March 29, 1943")
    print(person1)
    
    
                     
############Python 3
##### Classes Section
class AddressBook:
    """
    AddressBook instances hold and manage a list of people
    """
    pass
    
    
class AddressEntry:
    """
    AddressEntry instances hold and manage details of a person
    """
    def __init__(self, first_name=None, family_name=None,
                 email_address=None, date_of_birth=None):
          """Initialize attributes first_name,
          family_name and date_of_birth.
          Each argument should be a string.
          date_of_birth should be of the form "MM DD, YYYY"
          """
          self.first_name = first_name
          self.family_name = family_name
          self.email_address = email_address
          self.date_of_birth = date_of_birth
          
          
##### Main Section

if __name__ == "__main__":
    address_book = AddressBook()
    person1 = AddressEntry("Eric", "Idle", None, "March 29, 1943")
    print(person1)

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

Page 252-253
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.
However, in Python 3 you don’t need to add “(object)” when defining a class – it is assumed.
It is not an error to add (object) in Python 3, it just doesn’t look as nice.

        
#################Python2.7
    
class AddressEntry(object):
    """
    AddressEntry instances hold and manage details of a person
    """
    def __init__(self, first_name=None, family_name=None,
                  email_address=None, date_of_birth=None):
        """initialize attributes first_name, family_name
            and date_of_birth
        each argument should be a string
        date_of_birth should be of the form "MM DD, YYYY"
        """
        self.first_name = first_name
        self.family_name = family_name
        self.email_address = email_address
        self.date_of_birth = date_of_birth
        
    def __repr__(self):
        """
        Given an AddressEntry object self return
        a readable string representation
        """
        template = "AddressEntry(first_name='%s', "+\
                   "family_name='%s',"+\
                   " email_address='%s', "+\
                   "date_of_birth='%s')"
        return template%(self.first_name, self.family_name,
                          self.email_address, self.date_of_birth)
                          
##### Functions Section

##### Main Section

if __name__ == "__main__":
    address_book = AddressBook()
    person1 = AddressEntry("Eric", "Idle", None, "March 29, 1943")
    print(person1)
    
    
#################Python3    
class AddressEntry:
    """
    AddressEntry instances hold and manage details of a person
    """
    def __init__(self, first_name=None, family_name=None,
                  email_address=None, date_of_birth=None):
        """initialize attributes first_name, family_name
            and date_of_birth
        each argument should be a string
        date_of_birth should be of the form "MM DD, YYYY"
        """
        self.first_name = first_name
        self.family_name = family_name
        self.email_address = email_address
        self.date_of_birth = date_of_birth
        
    def __repr__(self):
        """
        Given an AddressEntry object self return
        a readable string representation
        """
        template = "AddressEntry(first_name='%s', "+\
                   "family_name='%s',"+\
                   " email_address='%s', "+\
                   "date_of_birth='%s')"
        return template%(self.first_name, self.family_name,
                          self.email_address, self.date_of_birth)
                          
##### Functions Section

##### Main Section

if __name__ == "__main__":
    address_book = AddressBook()
    person1 = AddressEntry("Eric", "Idle", None, "March 29, 1943")
    print(person1)

Page 254-256
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.
However, in Python 3 you don’t need to add “(object)” when defining a class – it is assumed.
It is not an error to add (object) in Python 3, it just doesn’t look as nice.

    
"""
Addressbook.py
An address book program to store details of people I know.
Stuff I'm storing is:
first name
family name
email address
date of birth
[other stuff]
Brendan Scott
Feb 2015
"""


##### Classes Section
class AddressBook(object):
    """
    AddressBook instances hold and manage a list of people
    """
    def __init__(self):
        """ Set people attribute to an empty list"""
        self.people = []
    def add_entry(self, new_entry):
        """ Add a new entry to the list of people in the
        address book the new_entry should be an instance
        of the AddressEntry class"""
        self.people.append(new_entry)
        
        
class AddressEntry(object):
    """
    AddressEntry instances hold and manage details of a person
    """
    def __init__(self, first_name=None, family_name=None,
                email_address=None, date_of_birth=None):
        """Initialize attributes first_name,
        family_name and date_of_birth.
        Each argument should be a string.
        date_of_birth should be of the form "MM DD, YYYY"
        """
        self.first_name = first_name
        self.family_name = family_name
        self.email_address = email_address
        self.date_of_birth = date_of_birth
    def __repr__(self):
        """
        Given an AddressEntry object self return
        a readable string representation
        """
        template = "AddressEntry(first_name='%s', "+\
                  "family_name='%s',"+\
                  "email_address='%s', "+\
                  "date_of_birth='%s')"
        return template%(self.first_name, self.family_name,
                         self.email_address, self.date_of_birth)
                         
##### Functions Section

##### Main Section

if __name__ == "__main__":
    address_book = AddressBook()
    person1 = AddressEntry("Eric", "Idle", None, "March 29, 1943")
    print(person1)
    address_book.add_entry(person1)
    print(address_book.people)
                              

                              
#################Python3                              
                              
"""
Addressbook.py
An address book program to store details of people I know.
Stuff I'm storing is:
first name
family name
email address
date of birth
[other stuff]
Brendan Scott
Feb 2015
"""


##### Classes Section
class AddressBook:
    """
    AddressBook instances hold and manage a list of people
    """
    def __init__(self):
        """ Set people attribute to an empty list"""
        self.people = []
    def add_entry(self, new_entry):
        """ Add a new entry to the list of people in the
        address book the new_entry should be an instance
        of the AddressEntry class"""
        self.people.append(new_entry)
        
        
class AddressEntry:
    """
    AddressEntry instances hold and manage details of a person
    """
    def __init__(self, first_name=None, family_name=None,
                email_address=None, date_of_birth=None):
        """Initialize attributes first_name,
        family_name and date_of_birth.
        Each argument should be a string.
        date_of_birth should be of the form "MM DD, YYYY"
        """
        self.first_name = first_name
        self.family_name = family_name
        self.email_address = email_address
        self.date_of_birth = date_of_birth
    def __repr__(self):
        """
        Given an AddressEntry object self return
        a readable string representation
        """
        template = "AddressEntry(first_name='%s', "+\
                  "family_name='%s',"+\
                  "email_address='%s', "+\
                  "date_of_birth='%s')"
        return template%(self.first_name, self.family_name,
                         self.email_address, self.date_of_birth)
                         
##### Functions Section

##### Main Section

if __name__ == "__main__":
    address_book = AddressBook()
    person1 = AddressEntry("Eric", "Idle", None, "March 29, 1943")
    print(person1)
    address_book.add_entry(person1)
    print(address_book.people)

Page 256

The code on this page is a little different. As explained in Project 7, the syntax for the open()
builtin is the same in Python 2.7 and Python 3, but Python 3 treats the files it opens differently
In code on this page, you are opening a file to write a “pickle” to. In Python 3 you need to open
the file with a “binary” modifier. That is, you need to use ‘wb’ rather than ‘w’. Similarly, to read
from a pickle file you need to open it using ‘rb’ rather than ‘r’.
When reading the pickle file, Python3 uses a different way of representing the data.
How it’s different is not relevant to the project, so you can ignore it. All you need
to do is notice that pickle has written something to the file and it’s encoded in some way.

    
#Python2.7
>>> import pickle
>>> FILENAME = "p4k_test.pickle"
>>>  dummy_list = [x*2 for x in range(10)]
>>>  dummy_list # confirm what it looks like
[0,  2, 4, 6, 8, 10, 12, 14, 16, 18]
>>>  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())
        
(lp0
I0
aI2
aI4
aI6
aI8
aI10
aI12
aI14
aI16
aI18
a.

#Python3
>>> import pickle
>>> FILENAME = "p4k_test.pickle"
>>>  dummy_list = [x*2 for x in range(10)]
>>>  dummy_list # confirm what it looks like
[0,  2, 4, 6, 8, 10, 12, 14, 16, 18]
>>>  with open(FILENAME,'wb') 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,'rb') as file_object: # change w to r!!!
        print(file_object.read())        

b'\x80\x03]q\x00(K\x00K\x02K\x04K\x06K\x08K\nK\x0cK\x0eK\x10K\x12e.'
>>>         

Page 258
Code is slightly different. As explained above, use ‘rb’ to open the file, rather than ‘r’

    
## Python2.7
Python 2.7.3 (default, Apr 14 2012, 08:58:41) [GCC] on linux2
Type "copyright", "credits" or "license()" for more information.
>>> import pickle
>>> FILENAME = "p4k_test.pickle"
>>> with open(FILENAME,'r') as file_object:
          dummy_copy = pickle.load(file_object)
          
>>> dummy_copy
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

## Python3
Python 3.3.5 (default, Mar 27 2014, 17:16:46) [GCC] on linux
Type "copyright", "credits" or "license()" for more information.
>>> import pickle
>>> FILENAME = "p4k_test.pickle"
>>> with open(FILENAME,'rb') as file_object:
          dummy_copy = pickle.load(file_object)

          
>>> dummy_copy
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> 

Page 260
Python3 now uses cPickle when you import pickle. If it can’t find cPickle
then it falls back to using the old pickle. So don’t worry about cPickle if you’re using Python 3.
As explained above, also use ‘wb’ to open the file, rather than ‘w’

    
# Python 2.7
#### Imports
import cPickle as pickle

#### Constants
SAVE_FILE_NAME = "address_book.pickle"

                          
    def save(self):
        with open(SAVE_FILE_NAME, 'w') as file_object:
              pickle.dump(self, file_object)
              
# Python 3
#### Imports
import pickle

#### Constants
SAVE_FILE_NAME = "address_book.pickle"

                          
    def save(self):
        with open(SAVE_FILE_NAME, 'wb') as file_object:
              pickle.dump(self, file_object)

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

Page 262
Code is different:
* As explained above, use ‘rb’ to open the file, rather than ‘r’

    
#Python 2.7

    def load(self):
        """
        Load a pickled address book from the standard save file
        """
        with open(SAVE_FILE_NAME, 'r') as file_object:
            self.address_book = pickle.load(file_object)
            
#Python 3

    def load(self):
        """
        Load a pickled address book from the standard save file
        """
        with open(SAVE_FILE_NAME, 'rb') as file_object:
            self.address_book = pickle.load(file_object)
#Python 3
    
"""
Addressbook.py
An address book program to store details of people I know.
Stuff I'm storing is:
first name
family name
email address
date of birth
[other stuff]

Brendan Scott
Feb 2015
"""

#### Imports
import pickle

#### Constants
SAVE_FILE_NAME = "address_book.pickle"

##### Classes Section
class AddressBook(object):
    """
    AddressBook instances hold and manage a list of people
    """
    def __init__(self):
        """ Set people attribute to an empty list"""
        self.people = []
        
    def add_entry(self, new_entry):
        """ Add a new entry to the list of people in the
        address book the new_entry should be an instance
        of the AddressEntry class"""
        self.people.append(new_entry)
        
    def save(self):
        """ save a copy of self into a pickle file"""
        with open(SAVE_FILE_NAME, 'wb') as file_object:
            pickle.dump(self, file_object)
            
            
class AddressEntry(object):
    """
    AddressEntry instances hold and manage details of a person
    """
    def __init__(self, first_name=None, family_name=None,
                  email_address=None, date_of_birth=None):
        """Initialize attributes first_name,
        family_name and date_of_birth.
        Each argument should be a string.
        date_of_birth should be of the form "MM DD, YYYY"
        """
        self.first_name = first_name
        self.family_name = family_name
        self.email_address = email_address
        self.date_of_birth = date_of_birth
        
    def __repr__(self):
        """
        Given an AddressEntry object self return
        a readable string representation
        """
        template = "AddressEntry(first_name='%s', "+\
                    "family_name='%s',"+\
                    "email_address='%s', "+\
                    "date_of_birth='%s')"
        return template%(self.first_name, self.family_name,
                          self.email_address, self.date_of_birth)
                          
                          
class Controller(object):
    """
    Controller acts as a way of managing the data stored in
    an instance of AddressBook and the user, as well as managing
    loading of stored data
    """
    def __init__(self):
        """
        Initialise controller. Look for a saved address book
        If one is found,load it, otherwise create an empty
        address book.
        """
        self.address_book = AddressBook()
        person1 = AddressEntry("Eric", "Idle", "March 29, 1943")
        self.address_book.add_entry(person1)
        
    def load(self):
        """
        Load a pickled address book from the standard save file
        """
        with open(SAVE_FILE_NAME, 'rb') as file_object:
            self.address_book = pickle.load(file_object)
                    
##### Functions Section

##### Main Section
if __name__ == "__main__":
    controller = Controller()
    print(controller.address_book.people)

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

Page 267
Code is different:
* import pickle (not import cPickle as pickle)
* As explained above, use ‘rb’ to open the file, rather than ‘r’

           
#Python 3
#### Imports
import pickle
import os.path


#### Constants
SAVE_FILE_NAME = "address_book.pickle"


class Controller(object):
    """
    Controller acts as a way of managing the data stored in
    an instance of AddressBook and the user, as well as managing
    loading of stored data
    """
    def __init__(self):
        """
        Initialize controller. Look for a saved address book
        If one is found,load it, otherwise create an empty
        address book.
        """
        self.address_book = self.load()
        if self.address_book is None:
            self.address_book = AddressBook()
              
    def load(self):
        """
        Load a pickled address book from the standard save file
        """
        #TODO: Test this method
        if os.path.exists(SAVE_FILE_NAME):
            with open(SAVE_FILE_NAME, 'rb') as file_object:
                return pickle.load(file_object)
        else:
            return None

Page 270-272
This code uses raw_input. As explained in Python 3/Project 3 you need to either replace
all references to raw_input with just input or add new line raw_input = input
in the Constants section

            
#Python 3

#### Constants
raw_input = input
SAVE_FILE_NAME = "address_book.pickle"
INSTRUCTIONS = """Address Book Application
(Python For Kids For Dummies Project 9)
Press:
a to add an entry
d to display a list of all entries in summary form.
i to print these instructions again
q to quit.
"""
CONFIRM_QUIT_MESSAGE = 'Are you sure you want to quit (Y/n)? '


    def __init__(self):
        """
        Initialize controller. Look for a saved address book
        If one is found,load it, otherwise create an empty
        address book.
        """
        self.address_book = self.load()
        if self.address_book is None:
            self.address_book = AddressBook()
        self.run_interface()

    def run_interface(self):
        """ Application's main loop.
        Get user input and respond accordingly"""
        print(INSTRUCTIONS)
        while True:
            command = raw_input("What would you like to do? ")
            if command == "a":
                self.add_entry()
            elif command == "q":
                if confirm_quit():
                    print("Saving")
                    self.address_book.save()
                    print("Exiting the application")
                    break
            elif command == "i":
                print(INSTRUCTIONS)
            elif command == "d":
                self.display_summaries()
            else:
                template = "I don't recognise that instruction (%s)"
                print(template%command)
                
    def add_entry(self):
        """query user for values to add a new entry"""
        print("In add_entry")

    def display_summaries(self):
        """ display summary information for each entry in
        address book"""
        print("In display_summaries")

Page 274
Changes necessary for the code to work in Python 3:
* imports cPickle – just import pickle
* uses raw_input – Need to add a line raw_input = input in the Constants section
* uses ‘r’ and ‘w’ when opening files for pickle – need to use ‘rb’ and ‘wb’

#Python 3

"""
Addressbook.py
An address book program to store details of people I know.
Stuff I'm storing is:
first name

family name
email address
date of birth
[other stuff]
Brendan Scott
Feb 2015
"""


#### Imports
import pickle
import os.path


#### Constants
raw_input = input
SAVE_FILE_NAME = "address_book.pickle"
INSTRUCTIONS = """Address Book Application
(Python For Kids For Dummies Project 9)
Press:
a to add an entry
d to display a list of all entries in summary form.
i to print these instructions again
q to quit.
"""
CONFIRM_QUIT_MESSAGE = 'Are you sure you want to quit (Y/n)? '
SUMMARY_TEMPLATE = "%s %s DOB: %s email: %s"

##### Classes Section
class AddressBook(object):
    """
    AddressBook instances hold and manage a list of people
    """
    def __init__(self):
        """ Set people attribute to an empty list"""
        self.people = []
        
    def add_entry(self, new_entry):
        """ Add a new entry to the list of people in the
        address book the new_entry should be an instance                                      
        of the AddressEntry class"""
        self.people.append(new_entry)
        
    def save(self):
        """ save a copy of self into a pickle file"""
        with open(SAVE_FILE_NAME, 'wb') as file_object:
              pickle.dump(self, file_object)
              
              
class AddressEntry(object):
    """
    AddressEntry instances hold and manage details of a person
    """
    def __init__(self, first_name=None, family_name=None,
                    email_address=None, date_of_birth=None):
        """Initialize attributes first_name,
        family_name and date_of_birth.
        Each argument should be a string.
        date_of_birth should be of the form "MM DD, YYYY"
        """
        self.first_name = first_name
        self.family_name = family_name
        self.email_address = email_address
        self.date_of_birth = date_of_birth
        
    def __repr__(self):
        """
        Given an AddressEntry object self return
        a readable string representation
        """
        template = "AddressEntry(first_name='%s', "+\
                      "family_name='%s',"+\
                      " email_address='%s', "+\
                      "date_of_birth='%s')"
        return template%(self.first_name, self.family_name,
                          self.email_address, self.date_of_birth)
                            
                            
class Controller(object):
    """
    Controller acts as a way of managing the data stored in
    an instance of AddressBook and the user, as well as managing
    loading of stored data
    """
    def __init__(self):
        """
        Initialize controller. Look for a saved address book
        If one is found,load it, otherwise create an empty
        address book.
        """
        self.address_book = self.load()
        if self.address_book is None:
            self.address_book = AddressBook()
        self.run_interface()
        
    def load(self):
        """
        Load a pickled address book from the standard save file
        """
        if os.path.exists(SAVE_FILE_NAME):
            with open(SAVE_FILE_NAME, 'rb') as file_object:
                address_book = pickle.load(file_object)
            return address_book
        else:
            return None
            
    def run_interface(self):
        """ Application's main loop.
        Get user input and respond accordingly"""
        print(INSTRUCTIONS)
        while True:
            command = raw_input("What would you like to do? ")
            if command == "a":
                self.add_entry()
            elif command == "q":
                if confirm_quit():
                    print("Saving")
                    self.address_book.save()
                    print("Exiting the application")
                    break
            elif command == "i":
                print(INSTRUCTIONS)
            elif command == "d":
                self.display_summaries()
            else:
                template = "I don't recognise that instruction (%s)"
                print(template%command)
                
    def add_entry(self):
        """query user for values to add a new entry"""
        print("Adding a new person to the address book")
        print("What is the person's:")
        first_name = raw_input("First Name? ")
        if first_name == "q":
            print("Not Adding")
            return
        family_name = raw_input("Family Name? ")
        if family_name == "q":
            print("Not Adding")
            return
        email_address = raw_input("Email Address? ")
        if email_address == "q":
            print("Not Adding")
            return
        DOB_PROMPT = "Date of Birth (Month day, year)? "
        date_of_birth = raw_input(DOB_PROMPT)
        if date_of_birth == "q":
            print("Not Adding ")
            return
        entry = AddressEntry(first_name, family_name,
                             email_address, date_of_birth)
        self.address_book.add_entry(entry)
        values = (first_name, family_name)
        print("Added address entry for %s %s\n"%values)
            
    def display_summaries(self):
        """ display summary information for each entry in
        address book"""
        print("Displaying Summaries")
        for index, e in enumerate(self.address_book.people):
            values = (e.first_name, e.family_name,
                      e.date_of_birth, e.email_address)
            entry = SUMMARY_TEMPLATE%values
            print("%s: %s"%(index+1, entry))
            # start numbering at 1
           
           
##### Functions Section
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
        
        
##### Main Section
if __name__ == "__main__":
    controller = Controller()