Chapter 1

  1. Computers use binary numbers because it's easier to build electronic devices reliably if they only have to distinguish between two electric states.
    1. 6 = 110
    2. 44 = 101100
    3. 72 = 1001000
    4. 131 = 10000011
    1. 100 = 4
    2. 1011 = 11
    3. 101010 = 42
    4. 1001110 = 78
    1. Make the cookie batter:
      • Mix the dry ingredients.
      • Cream the butter and sugar.
      • Beat in the eggs.
      • Stir in the dry ingredients.
    2. Bake the cookies:
      • Set the oven for the appropriate temperature.
      • Set the timer.
      • Place the cookies into the oven.
      • Allow the cookies to bake.
    3. Add frosting and sprinkles:
      • Mix the ingredients for the frosting.
      • Spread frosting and sprinkles onto the cookies.
  2. The legal identifiers shown are printed, annual_salary, abc, sum_of_data, and b4.
  3. c. print("Hello, world!")
  4. Output of statements:

    "Quotes"
    Slashes \//
    How '"confounding' "\" it is!
    
  5. Output of statements:

    name    age     height
    Archie  17      5'9"
    Betty   17      5'6"
    Jughead 16      6'
    
  6. Output of statements:

    Shaq is 7'1
    The string "" is an empty message.
    \'""
    
  7. Output of statements:

        a   b   c"
    \\
    "
    '''
    C:
    in  he downward spiral
    
  8. Output of statements:

    Dear "DoubleSlash" magazine,
    
        Your publication confuses me.  Is it
    a \\ slash or a //// slash?
    
    Sincerely,
    Susan "Suzy" Smith
    
  9. print statements to produce desired output:

    print("\"Several slashes are sometimes seen,\"")
    print("said Sally. \"I've said so.\" See?")
    print("\\ / \\\\ // \\\\\\ ///")
    
  10. print statements to produce desired output:

    print("This is a test of your")
    print('knowledge of "quotes" used')
    print("in 'string literals.'")
    print()
    print("You're bound to \"get it right\"")
    print("if you read the section on")
    print("''quotes.''")
    
  11. print statement to produce desired output:

    print("/ \\ // \\\\ /// \\\\\\")
    
  12. Mistakes in the program:

    1. line 1: The keyword def is missing.
    2. line 2: A closing ) is missing.
    3. line 3: The text inside the parentheses should be in quotes.
  13. Mistakes in the program:

    1. line 2: Should be indented.
    2. line 3: Should be indented.
    3. line 3: A closing " mark is missing.
  14. a. def example():
  15. Output of the program:

    This is message1.
    This is message2.
    This is message1.
    Done with message2.
    Done with main.
            
  16. Output of the program:

    Inside first function
    Inside third function
    Inside first function
    Inside second function
    Inside first function
    Inside second function
    Inside first function
    Inside third function
    Inside first function
    Inside second function
    Inside first function
            
  17. Output of the program:

    Inside first function
    Inside first function
    Inside second function
    Inside first function
    Inside third function
    Inside second function
    Inside first function
    Inside first function
    Inside second function
    Inside first function
    Inside third function
            
  18. Output of the program:

    Inside second function
    Inside first function
    Inside first function
    Inside second function
    Inside first function
    Inside third function
    Inside first function
    Inside second function
    Inside first function
            
  19. Mistakes in the program:

    1. line 2: The line is indented in one too far.
    2. line 2: The contents of the parentheses aren't in quotes.
    3. line 3: The function call is misspelled.
    4. line 5: Parentheses are missing after the function name.
    5. line 5: A : is missing after the function name and parentheses.
    6. line 6: A closing ) is missing.
    7. line 7: The "s inside the string need to be escaped.
    8. line 7: The line shouldn't include a ;.

Chapter 2

  1. Results of expressions:

    1. 8
    2. 11
    3. 6
    4. 4
    5. 33
    6. -16
    7. 6.4
    8. 6
    9. 30
    10. 1
    11. -1
    12. 5.0
    13. 2
    14. 18
    15. 3
    16. 4
    17. 4
    18. 15
    19. 8
    20. 1
  2. Results of expressions:

    1. 9.0
    2. 9.6
    3. 2.0
    4. 6.0
    5. 6.0
    6. 8.0
    7. 1.25
    8. 3.0
    9. 3.0
    10. 3.0
    11. 5.0
    12. 6.4
    13. 37.0
    14. 8.5
    15. 9.6
    16. 4.0
    17. 4.8
    18. 25.5
  3. e. grade = 4.0
  4. Last digit: number % 10
  5. Mistakes in the program:

    1. line 3: There should be a , between is" and x.
    2. line 6: The , x should be outside the quotes.
    3. line 9: The word int should be omitted.
    4. line 11: The word and should be surrounded by quotes.
  6. Values of a, b, and c after the code:

                a: 6
                b: 9
                c: 16
                
  7. Values of first and second after the code:

                first: 19
                second: 8
                
    The code swaps the values of the variables first and second.
  8. Rewritten shortened version of the code:

    first = 8,
    second = 19
    first += second
    second = first - second
    first -= second
    
  9. Values of i, j, and k after the code:

    i: 4
    j: 2
    k: 1
    
  10. Output of code:

    46
    36
    23
    13
    
  11. Version of the program that uses variables to avoid redundant expressions:

    def main():
        # Calculate my pay at work, based on how many hours I worked each day
        total_hours = 4 + 5 + 8 + 4
        salary = 8.75
        pay = total_hours * salary
        tax_rate = 0.20
        taxes_owed = pay * tax_rate
                        
        print("My total hours worked:")
        print(total_hours)
        print("My hourly salary:")
        print("$" + str(salary))
        print("My total pay:")
        print(pay)
        print("My taxes owed:")
        print(taxes_owed)
    
    main()
    
  12. def main():
        for i in range(1, 5):
            print("2 times", i, "=", (2 * i))
            
    main()
    
    1. 2 * count
    2. 15 * count - 11
    3. -10 * count + 40
    4. 4 * count - 11
    5. -3 * count + 100
  13. for i in range(1, 7):
        print(18 * i - 22)
    
  14. Output of odd_stuff function:

    4
    2
    1
    0
    
  15. Output of loop:

    9 1
    7 2
    4 3
    0 4
    
  16. Output of loop:

    +---+
    \   /
    /   \
    \   /
    /   \
    \   /
    /   \
    +---+
    
  17. Output of loop:

    T-minus 5, 4, 3, 2, 1, Blastoff!
    
  18. Output of loops:

    1 2 3 4 5 6 7 8 9 10 
    2 4 6 8 10 12 14 16 18 20 
    3 6 9 12 15 18 21 24 27 30 
    4 8 12 16 20 24 28 32 36 40 
    5 10 15 20 25 30 35 40 45 50
    
  19. Output of loops:

    ****!****!****!
    ****!****!****!
    
    1. 2 * line + 2 * SIZE
    2. 4 * line + (3 * SIZE)
    3. -line + (2 * SIZE + 3)
  20. Table for output:

    line\!/
    10220
    22182
    34144
    46106
    5868
    610210
  21. Table for output:

    line\!/
    10140
    22102
    3464
    4626

Chapter 3

  1. 15 42
    10 25
    
  2. 1 3 5 
    1 3 5 7 9 11 13 15 
    1 3 5 7 9 11 13 15 17 19 21 23 25 
    
  3. 1 2 3 4 5 
    1 2 3 4 5 6 7 
    1 2 3 4 
    number = 8
    
  4. whom and who like it
    it and him like whom
    whom and him like him
    stu and boo like who
    her and him like who
    
  5. touch your eye to your head
    touch your head to your eye
    touch your shoulders to your elbow
    touch your eyes and ears to your eyes and ears
    touch your toes to your Toes
    touch your shoulders to your knees toes
    
  6. say coke not pepsi or pop
    say soda not soda or pepsi
    say pepsi not koolaid or pop
    say say not pepsi or pepsi
    
  7. def print_strings(s, n):
        for i in range(1, n + 1):
            print(s, end=" ")
        print()
    
  8. The Temperature program changes the value of its tempc parameter on line 9, but this doesn't affect the variable tempc in main. The (incorrect) output is:
    Body temp in C is: 0.0
    
  9. results of math expressions:

    1. 1.6
    2. 2
    3. 36.0
    4. 64.0
    5. 10.0
    6. 116.0
    7. 7
    8. 5
    9. -5
    10. 8.0
    11. 11.0
    12. 102.0
    13. 17.0
    14. 20.0
    15. 13.0
    16. 14.0
  10. Output of the program:

    3 0
    1 2 4
    4 3
    5 2 4
    8 1
    5 9 4
    
  11. grade = 2.7
    math.round(grade)
                               # grade = 2.7
    grade = math.round(grade)
                               # grade = 3.0
    
    min = math.min(grade, math.floor(2.9))
                               # min = 2.0
    x = math.pow(2, 4)
                               # x = 16.0
    x = math.sqrt(64)
                               # x = 8.0
    count = 25
    math.sqrt(count)
                               # count = 25
    count = math.sqrt(count)
                               # count = 5
    
    a = math.abs(math.min(-1, -3))
                               # a = 3
    
  12. def count_quarters(cents):
        return cents % 100 // 25
    
  13. def main():
        age = int(input("How old are you?"))
        print("You have", (65 - age), "years until retirement.")
    
    main()
    
  14. number = int(input("Type an integer: "))
    print(number, "times 2 =", (number * 2))
    
  15. phrase = input("What is your phrase? ")
    times = int(input("How many times should I repeat it? "))
    for i in range(1, times + 1): 
        print(phrase)
    
  16. Correct syntax to draw a rectangle:

    e. p.draw_rect(10, 20, 50, 30)
  17. Mistakes in the code:

    1. On the second line, the call to draw_line should be made on the DrawingPanel p, not on nothing.
    2. On the second line, the order of the parameters is incorrect.

    The following is the corrected code:

    p = DrawingPanel(200, 200)
    p.draw_line(50, 86, 20, 35)
    
  18. The black rectangle is being drawn second, so it's covering up the white inner circle. The following code fixes the problem:

    panel = DrawingPanel(200, 100)
    p.set_color("black") 
    p.fill_rect(10, 10, 50, 50)
    p.set_color("white")
    p.fill_oval(10, 10, 50, 50) 
    
  19. The program draws a series of progressively smaller black circles, each with its right and bottom edges touching the right and bottom corners of the window. Its output looks like this:

    image

Chapter 4

  1. English statements translated into logical tests:

    1. z % 2 == 1
    2. z <= math.sqrt(y)
    3. y > 0
    4. x % 2 != y % 2
    5. y % z == 0
    6. z != 0
    7. abs(y) > abs(z)
    8. (x >= 0) == (z < 0)
    9. y % 10 == y
    10. z >= 0
    11. x % 2 == 0
    12. abs(x - y) < abs(z - y)
  2. Results of relational expressions:

    1. True
    2. False
    3. True
    4. False
    5. True
    6. False
    7. False
    8. True
    9. True
  3. Mistakes in the program:

    1. line 5: = should be ==, and line should end with a colon
    2. line 5: smaller is out of scope here (line 4 should be smaller = minimum(a, b)
    3. line 10: => should be >= (or better yet, no if test is needed)
  4. Output of if_else_mystery_1 calls:

    Call Output
    a. if_else_mystery_1(3, 20) 13 21
    b. if_else_mystery_1(4, 5) 5 6
    c. if_else_mystery_1(5, 5) 6 5
    d. if_else_mystery_1(6, 10) 7 11
  5. Output of if_else_mystery_2 calls:

    Call Output
    a. if_else_mystery_2(10, 2) 10 6
    b. if_else_mystery_2(3, 8) 9 9
    c. if_else_mystery_1(4, 4)2 3 4
    d. if_else_mystery_2(10, 30) 29 30
  6. Code to read an integer from the user, then print even if that number is an even number or odd otherwise:

    number = int(input("Type a number: "))
    if number % 2 == 0:
        print("even")
    else:
        print("odd")
    
  7. The code incorrectly prints that even numbers not divisible by 3 are odd. This is because the else statement matches the most closely nested if statement (number % 3 == 0), not the outer if statement. The following change corrects the problem.

    if number % 2 == 0:
        if number % 3 == 0:
            print("Divisible by 6.")
    else:
        print("Odd.")
    
  8. Refactored code to reduce redundancy:

    a = 2
    if x < 30:
        x += 1
    print("Python is awesome!", x)
    
  9. Refactored code to reduce redundancy:

    times = int(input("Is your money multiplied 1 or 2 times? "))
    donation = int(input("And how much are you contributing? "))
    sums += times * donation
    total += donation
    
    if times == 1:
        count1 += 1
    elif times == 2:
        count2 += 1
    

    If the user could type any number, the code might need additional if statements to increment the proper count variable.

  10. Code to read red/green/blue color:

    choice = input("What color do you want? ")
    if choice.lower() == "r":
        print("You have chosen Red.")
    elif choice.lower() == "g":
        print("You have chosen Green.")
    elif choice.lower() == "b":
        print("You have chosen Blue.")
    else:
        print("Unknown color:", choice)
    
  11. The problem with the given sum_to function is that the sum variable needs to be declared outside the for loop. The following code fixes the problem:

    def sum_to(n):
        sums = 0
        for i in range(1, n + 1):
            sums += i
        return sums
    
  12. The count_factors function shown will not return the right answer. It should count the factors using a a cumulative sum; it should not return inside the loop when it finds each factor. Instead, it should declare a counter outside the loop that is incremented as each factor is seen. The following code fixes the problem:

    def count_factors(n):
        count = 0
        for i in range(1, n + 1): 
            if n % i == 0:
                count += 1
        return count
    
  13. The expression equals 6.800000000000001 rather than the expected 6.8 because of a roundoff error.

  14. The expression gpa * 3 equals 9.600000000000001 rather than the expected 9.6 because of a roundoff error. A fix would be to test that the value is close to 9.6 rather than exactly equal to it, as shown in the following code:

    gpa = 3.2
    diff = abs(gpa * 3 - 9.6)
    if diff < 0.1:
        print("You earned enough credits.")
    
  15. The preconditions of print_triangle_type function are that the three side lengths constitute a valid triangle. Namely:

  16. The preconditions of the get_grade function are that the grade parameter's value is between 0 and 100.

  17. The median_of_3 code fails when n3 is the smallest of the three numbers; for example, when the parameters' values are (4, 7, 2), the code should return 4 but instead returns 2. The method could be correctly written as:

    def median_of_3(n1, n2, n3):
        if n1 < n2 and n1 < n3:
            if n2 < n3:
                return n2
            else:
                return n3
        elif n2 < n1 and n2 < n3:
            if n1 < n3:
                return n1
            else:
                return n3
        else: # n3 < n1 and n3 < n2
            if n1 < n2:
                return n1
            else:
                return n2
    

    or the following shorter version:

    def median_of_3(n1, n2, n3):
        return max(max(min(n1, n2), min(n2, n3)), min(n1, n3))
    
  18. The code fails when more than one number is odd, because it uses elif rather than if. The tests should not be nested because they are not mutually exclusive; more than one number could be odd. The following code fixes the problem:

    def print_num_odd(n1, n2, n3):
        count = 0
        if n1 % 2 != 0:
            count += 1
        if n2 % 2 != 0:
            count += 1
        if n3 % 2 != 0:
            count += 1
        print(count, "of the 3 numbers are odd.")
    

    The following version without if statements also works:

    def print_num_odd(n1, n2, n3):
        count = n1 % 2 + n2 % 2 + n3 % 2
        print(count, "of the 3 numbers are odd.")
    
  19. Output of the program:

    efg
    nopqrs
    
    qr
    
  20. Statement that tests to see whether a string begins with a capital letter:

    if "A" <= the_string[0] <= "Z":
        ...
    
  21. The following expression would produce the desired result:

    name = "Marla Singer"
    space = name.find(" ")
    last_name = name[(space + 1) :]
    first_initial = name[0]
    last_name_first_initial = last_name + ", " + first_initial + "."
    print(last_name_first_initial)
    

    Alternatively, you could use this shorter version:

    name = "Marla Singer"
    print(name[(name.find(" ") + 1):] + ", " + name[0] + ".")
    
  22. Code to examine a string and determine how many of its letters come from the second half of the alphabet ('n' or later):

    # assuming that the string is stored in the variable s
    count = 0
    for ch in s.lower():
        if ch >= 'n':
            count += 1
    print(count, "letters come after n.")
    

Chapter 5

    1. Executes body 10 times. Output is:
      1 11 21 31 41 51 61 71 81 91
    2. Executes body 0 times. No output.
    3. Loops infinitely. Output is:
      250
      250
      250
      ...
      
    4. Executes body 3 times. Output is:
      2 4 16
    5. Executes body 5 times. Output is:
      bbbbbabbbbb
    6. Executes body 7 times. Output is:
      10
      5
      2
      1
      0
      0
      0
      
    1. n = 1
      while n <= maxs:
          print(n)
          n += 1
      
    2. total = 25
      number = 1
      while number <= (total // 2):
          total = total - number
          print(total, number)
          number += 1
      
    3. i = 1
      while i <= 2:
          j = 1
          while j <= 3:
              k = 1
              while k <= 4:
                  print("*", end="")
                  k += 1
              print("!", end="")
              j += 1
          print()
          i += 1
      
  1. Output of mystery calls:
    Call Output
    a. mystery(1) 1 0
    b. mystery(6) 4 2
    c. mystery(19) 16 4
    d. mystery(39) 32 5
    e. mystery(74) 64 6
  2. Output of mystery calls:

    Call Output
    a. mystery(19) 19 0
    b. mystery(42) 21 1
    c. mystery(48) 3 4
    d. mystery(40) 5 3
    e. mystery(64) 1 6
  3. Code that generates a random integer between 0 and 10 inclusive:
    import random
    num = random.randint(0, 10)
    
  4. Code that generates a random odd integer between 50 and 99 inclusive.
    import random
    num = random.randint(0, 24) * 2 + 51
    
  5. The print_letters code has a fencepost problem; it will print a dash after the last letter. The following code corrects the problem:
    def print_letters(text):
        if len(text) > 0:
            print(text[0], end="")
            for i in range(1, len(text)): 
                print("-" + text[i], end="")
            print()   # to end the line of output
    
  6. Sentinel loop that repeatedly prompts the user to enter a number and, once the number -1 is typed, displays the maximum and minimum numbers that the user entered:
    SENTINEL = -1
    num = int(input("Type a number (or " + str(SENTINEL) + " to stop): "))
    mins = num
    maxs = num
    while num != SENTINEL:
        if num < mins:
            mins = num
        elif num > maxs:
            maxs = num
    
        num = int(input("Type a number (or " + str(SENTINEL) + " to stop): "))
        
    if mins != SENTINEL:
        print("Maximum was", maxs)
        print("Minimum was", mins)
    
  7. Results of boolean expressions:

    1. True
    2. True
    3. False
    4. True
    5. True
    6. False
    7. False
    8. True
    9. True
    10. True
    11. True
    12. False
  8. def is_vowel(c):
        c = c.lower()  # case-insensitive
        return c == "a" or c == "e" or c == "i" or c == "o" or c == "u"
    
    or:
    def is_vowel(c):
        return "aeiouAEIOU".find(c) >= 0
    
  9. In this is_prime code the boolean flag isn't being used properly, because if the code finds a factor of the number, prime will be set to False, but on the next pass through the loop, if the next number isn't a factor, prime will be reset to Tue again. The following code fixes the problem:
    def is_prime(n):
        prime = True
        for i in range(2, n): 
            if n % i == 0:
                prime = False
        return prime
    
  10. Improved version of start_end_same code using boolean zen:
    def start_end_same(string):
        return string[0] == string[-1]
    
  11. Improved version of has_pennies code using Boolean zen:
    def has_pennies(cents):
        return cents % 5 != 0
    
  12. Output of mystery calls:

    Call Value Returned
    a. mystery(3, 3) 3
    b. mystery(5, 3) 1
    c. mystery(2, 6) 2
    d. mystery(12, 18) 6
    e. mystery(30, 75) 15
  13. The Zune code will get stuck in an infinite loop when the current date is the end of a leap year. On December 31 of a leap year, the days value will be 366, so code enters the if is_leap_year statement but does not enter the if days > 366 statement. So the loop does not subtract any days and never terminates. It can be fixed by adding a boolean flag to the loop:
    days = get_total_days_since_1980()   
    year = 1980
    stop = False
    while days > 365 and not stop:   # subtract out years
        if is_leap_year(year):
            if days > 366:
                days -= 366
                year += 1
            else:               # new code here
                stop = True     # new code here
        else:
            days -= 365
            year += 1
    
  14. Negations of boolean expressions:
    1. not b
    2. (x <= y) or (y <= z)
    3. (x != y) and (x > z)
    4. (x % 2 == 0) or not b
    5. (x // 2 != 13) and not b and (z * 3 != 96)
    6. (z >= x) or (z <= y and x < y)
  15. The age/GPA reading code should reprompt for a valid integer for the user's age and a valid real number for the user's GPA. If the user types a token of the wrong type, the line of input should be consumed and the user should be reprompted. The following code implements the corrected behavior:
    success = False
    while not success:
        age = input("Type your age: ") 
        try:
            age = int(age)
            success = True
        except ValueError:
            print("Not an integer; try again.")
            
    success = False
    while not success:
        gpa = input("Type your GPA: ") 
        try:
            gpa = float(gpa)
            success = True
        except ValueError:
            print("Not a float; try again.")
    
    print("age =", age, " GPA = ", gpa)
    
  16. Write code that prompts for three integers, averages them, and prints the average; robust against invalid input:
    prompt = "Please enter a number: "
    num1 = get_int(prompt)
    num2 = get_int(prompt)
    num3 = get_int(prompt)
    average = (num1 + num2 + num3) / 3.0
    print("Average:", average)
    
  17. Point B
    Point y < x y == 0 count > 0
    Point A SOMETIMES SOMETIMES NEVER
    ALWAYS SOMETIMES SOMETIMES
    Point C ALWAYS ALWAYS ALWAYS
    Point D SOMETIMES SOMETIMES SOMETIMES
    Point E NEVER SOMETIMES SOMETIMES
  18. Point B
    Point n > b a > 1 b > a
    Point A SOMETIMES SOMETIMES SOMETIMES
    ALWAYS SOMETIMES SOMETIMES
    Point C SOMETIMES ALWAYS ALWAYS
    Point D SOMETIMES ALWAYS NEVER
    Point E NEVER SOMETIMES SOMETIMES
  19. Point next == 0 prev == 0 next == prev
    Point A SOMETIMES ALWAYS SOMETIMES
    Point B NEVER SOMETIMES SOMETIMES
    Point C NEVER NEVER ALWAYS
    Point D SOMETIMES NEVER SOMETIMES
    Point E ALWAYS SOMETIMES SOMETIMES

Chapter 6

  1. A file is a named collection of information stored on a computer. We can read a file by opening it and then using the read or readlines method:

    with open("input.txt") as file:
        lines = name.readlines()
    
  2. with open("input.txt") as file:
        lines = name.readlines()
    
  3. The call to split returns:

    c. ["welcome...to", "the", "matrix."]
  4. The call to split returns:

    b. ["in", "fourteen-hundred", "92", "columbus", "sailed", "the", "ocean", "blue", ":)"]
    1. "numbers.dat" or "C:/Users/yana/Documents/programs/numbers.dat"
    2. "data/homework6/input.dat" or "C:/Users/yana/Documents/programs/data/homework6/input.dat"
    3. There is only one legal way to refer to this file: by its absolute path, "C:/Users/yana/Documents/homework/data.txt"
    1. "names.txt" or "/home/yana/Documents/hw6/names.txt"
    2. "data/numbers.txt" or "/home/yana/Documents/hw6/data/numbers.txt"
    3. There is only one legal way to refer to this file: by its absolute path, "/home/yana/download/saved.html"
  5. The following output would be produced:

    input: 6.7        This file has
    input:         several input lines.
    input: 
    input:   10 20         30   40
    input: 
    input: test
    6 total
    
  6. Output produced if readlines and split are used:

    input: 6.7
    input: This
    input: file
    input: has
    input: several
    input: input
    input: lines.
    input: 
    input: 10
    input: 20
    input: 30
    input: 40
    input: 
    input: test
    14 total
    
  7. Program that prints itself as output:

    def main():
        with open("input.txt") as file:
            lines = file.readlines()
            for line in lines:
                print(line.strip())
    
    main()
    
  8. Code that prompts the user for a file name and prints the contents of that file to the console as output:

    def print_entire_file():
        file_name = input("Type a file name: ")
        with open(file_name) as file:
            lines = file.readlines()
            for line in lines:
                print(line.strip())
    
  9. Program that takes as input lines of text and produces as output the same text inside a box:

    # precondition: no line in input is longer than width
    def print_box(lines, width):
        print_top_bottom(width)
        for line in lines:
            spaces = width - len(line.strip())
            print("| " + line.strip() + (" " * spaces) + " |")
        print_top_bottom(width)
    
    def print_top_bottom(width):
        print("+", end="")
        for i in range(0, width + 2): 
            print("-", end="")
        print("+")
    
  10. Code to print the following four lines of text into a file named message.txt:

    with open("message.txt", "w") as my_file:
        print("Testing,", file=my_file)
        print("1, 2, 3.", file=my_file)
        print("", file=my_file)
        print("This is my output file.", file=my_file)
    
  11. Code that repeatedly prompts the user for a file name until the user types the name of a file that exists on the system.

    import os.path
    
    def get_file_name():
        file_name = ""
        while not os.path.isfile(file_name):
            file_name = input("Type a file name: ")
        return file_name
    
  12. Code that uses get_file_name before calling print_entire_file:

    # reprompts until file name is valid
    def print_entire_file_2():
        file_name = get_file_name()
        with open(file_name) as file:
            lines = file.readlines()
            for line in lines:
                print(line.strip())
    

Chapter 7

  1. data = [27, 51, 33, -1, 101]
    
  2. Code that stores all odd numbers between -6 and 38 into an array using a loop:

    odds = [0] * 22
    for i in range(len(odds)): 
        odds[i] = i * 2 - 5
    
  3. After the code is executed, the numbers list contains the following element values:

    [0, 4, 11, 0, 44, 0, 0, 2]
    
  4. After the code is executed, the data list contains the following element values:

    [3, 3, 0, 0, 6, 9, 0, -18]
    
  5. data = [7, -1, 13, 24, 6]
    
    1. letters[:2]
    2. letters[3:4]
    3. letters[3:]
    4. letters[:3:-1]
    5. letters[1::2]
  6. def maximum(data):
        maxs = data[0]
        for i in range(1, len(data)): 
            if data[i] > maxs:
                maxs = data[i]
        return maxs
    
  7. def average(a):
        mean = 0.0
        for i in range(0, len(a)): 
            mean += a[i]
        return mean / len(a)
    
  8. data[3:3] = ["dark", "and"]
  9. data[1] = "IS"
  10. i = 0
    while i < len(data):
        if "a" in data[i]:
            data.pop(i)
        else:
            i += 1
    
  11. A list traversal is a sequential processing of each of an list's elements. Problems that can be solved in this manner include printing a list, comparing two lists for equality, and searching a list for a given value.

  12. Code that uses a for loop to print each element of a list named data:

    for i in range(len(data)): 
        print("element [", i, "] is", data[i])
    
  13. After the code is executed, the list array contains the following element values:

    [3, 24, 8, -6, 6, 1]
    
    1. [1, 2, 6, 8]
    2. [10, 30, 40, 20, 60, 50]
    3. [-4, 1, 25, 4, 16, 9, 64, 36, 49]
    1. [20, 10, 20, 30, 30, 20]
    2. [8, 7, 8, 2, 9, 7, 4, 4, 2, 8]
    3. [33, 28, 33, -1, 3, 28, 17, 9, 33, 17, -1, 33]
  14. def all_less(list1, list2):
        if len(list1) != len(list2):
            return False
        for i in range(len(list1)): 
            if list1[i] >= list2[i]:
                return False
        return True
    
  15. After the code is executed, the numbers list contains the following element values:

    [20, 30, 40, 50, 60, 70, 80, 90, 100, 100]
    
  16. After the code is executed, the numbers list contains the following element values:

    [10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
    
  17. After the mystery function is executed, the list a1 contains the following element values:

    [26, 19, 14, 11, 10]
    
  18. After the mystery2 function is executed, the list numbers contains the following element values:

    [7, 3, 1, 0, 25, 4, 18, -1, 5]
    
  19. Results of calls to mystery3 function:

    1. 0
    2. 9
    3. 6
    4. 8
    5. 2
  20. def average_length(strings):
        sums = 0
        for i in range(len(strings)): 
            sums += len(strings[i])
        return sums / len(strings)
    
  21. def is_palindrome(lis):
        for i in range(len(lis) // 2): 
            if lis[i] != lis[len(lis) - 1 - i]:
                return False
        return True
    
    1. [s[0].upper() for s in letters]
    2. [s + s for s in letters]
    3. [(s, s[0]) for s in letters]
  22. Output of the program:

    2 [0, 0, 1, 0]
    1 [0, 0, 1, 0]
    3 [0, 0, 1, 1]
    2 [0, 0, 1, 1]
    
  23. Output of the program:

    2 [0, 1]
    1 [0, 1]
    1 [1, 2]
    0 [1, 2]
    
  24. def swap_pairs(lis):
        for i in range(0, len(lis) - 1, 2): 
            swap(lis, i, i + 1)
    
  25. The four errors are:
    1. On line 2, you can't modify the value of a tuple by adding 1 to one of its elements.
    2. On line 4, you can't call append on a tuple.
    3. On line 6, you can't call reverse on a tuple.
    4. On line 9, you can't call clear on a tuple.
  26. def distance(p1, p2):
        dx = p2[0] - p1[0]
        dy = p2[1] - p1[1]
        return math.sqrt(dx * dx + dy * dy)
    
    def nearest_points(points):
        min_dist = 0
        min_p1 = (0, 0)
        min_p2 = (0, 0)
        for i in range(len(points)):
            for j in range(i + 1, len(points)):
                dist = distance(points[i], points[j])
                if min_dist == 0 or dist < min_dist:
                    min_dist = dist
                    min_p1 = points[i]
                    min_p2 = points[j]
        print(min_p1, "and", min_p2)
    
  27. After the code is executed, the numbers list contains the following element values:

    [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
    
  28. Loop to initialize the third row of data to store the numbers 1 through 7:

    for i in range(7): 
        data[2][i] = i + 1
    
  29. Code that constructs a two-dimensional list of integers with 5 rows and 10 columns, filled with a multiplication table:

    table = []
    for i in range(5):
        table.append([0] * 10)
    for i in range(5): 
        for j in range(10): 
            table[i][j] = i * j
    
  30. Loop to copy the contents of second column into fifth column:

    for i in range(6): 
        matrix[i][4] = matrix[i][1]
    
  31. After the mystery2d function is executed, the numbers list contains the following element values:

    [[4, 5, 6, 6], [5, 6, 7, 7], [6, 7, 8, 8]]
    
  32. jagged = []
    value = 1
    for i in range(5):
        jagged.append([0] * (i + 1))
        for j in range(i + 1):
            jagged[i][j] = value
            value += 1
    
  33. If the 2D array is named pixels, the panel's height is stored as len(pixels) and its width is stored as len(pixels[0]) .
  34. to_red_channel (TODO)

Chapter 8

  1. You can examine every element of a dictionary using a for-each loop. You can check whether a key is in the dictionary with the in keyword.

  2. If you try to add a key/value pair to a dictionary and it already has a pair with that same key, the old pair is replaced by the new pair. If it already has a pair with the same value, but not with the same key, the new pair is added without removing the old pair.

  3. You can loop over the keys of a dictionary in sorted order by passing the keys to the sorted function.

  4. Code to declare a dictionary that associates people's names with their ages:

    ages = {}
    ages["Stuart"] = 85
    ages["Marty"] = 12
    ages["Allison"] = 25
    

    or:

    ages = {"Stuart": 85, "Marty": 12, "Allison": 25}
    
  5. Keys and values contained in the dictionary after the code executes:

    {17: 'Steve', 34: 'Louann', 5: 'Moshe', 27: 'Donald', 2350: 'Orlando', 15: 'Moshe'}
    
  6. Keys and values contained in the dictionary after the code executes:

    {132: 'OneThreeTwo', 8: 'Ocho', 9828: 'Ninety-eight18', 8650: 'Eighty-sixForty-one', 50: 'Fifty', 79: 'Seventy-nine'}
    
  7. Output produced when the mystery method is passed each dictionary:

    1. {'one': 'un', 'four': 'quatre', 'deux': 'two', 'cinq': 'five', 'three': 'trois'}
    2. {'computer': 'program', 'car': 'drive', 'board': 'skate'}
    3. {'begin': 'ready', 'boy': 'girl', 'first': 'last', 'ebert': 'siskel', 'H': 'T'}
    4. {'cotton': 'rain', 'light': 'tree', 'tree': 'violin', 'seed': 'tree'}
  8. Result returned when the mystery function is passed each pair of dictionaries:

    1. {'mumble': 'fire', 'baz': 'wind', 'bar': 'earth', 'foo': 'air'}
    2. {'one': 'dos', 'five': 'quatro', 'three': 'tres'}
    3. {'c': 'seven', 'g': 'seven', 'b': 'years', 'e': 'ago'}
  9. Result returned when the mystery function is passed each list:

    1. {1: [1], 2: [1, 1], 3: [], 4: [1], 34: [], 14: []}
    2. {1: [1, 1, 1], 2: [1, 1], 4: [1, 1]}
    3. {43: [1], 44: [], 45: [1, 1], 54: [1]}
  10. Result returned when the mystery function is passed each dictionary:

    1. {3: {'and'}, 4: {'hello', 'world'}}
    2. {1: {'banana', 'kiwi'}, 2: {'peach'}, 3: {'apple', 'nectarine'}}
    3. {'the': {'is', 'has'}, 'and': {'the', 'and'}}
  11. start_letters TODO
  12. dict_comprehensions TODO
  13. You should use a set rather than a list if you wanted to avoid duplicates or wanted to be able to search the collection quickly.

  14. You can examine every element of a set using a for-each loop.

  15. You can check whether a value is contained in a set with the in keyword.

  16. After the code executes, the set contains the following elements (in some order):

    {32, 18274, 18212, 9, 12, 29999, 9074}
    
  17. After the code executes, the set contains the following elements (in some order):

    {4, 42, 11, 12, 84, 247, 94}
    
  18. To do a union, use set1 | set2 to create a new set with all items in both sets. To do an intersection, use set1 & set2 to create a new set with only items in both of the sets.

  19. To find all of the men who are not hungry and are old or poor or both: (male - hungry) & (old | poor)

  20. Output produced when the mystery function is passed each list:
    1. {'jessica', 'amanda', 'helene'}
    2. {'riley'}
    3. {'charlie', 'phil', 'alex', 'roy', 'tyler'}

Chapter 9

  1. Recursion is a technique where an algorithm is expressed in terms of itself. A recursive function differs from a regular function in that it contains one or more calls to itself within its body.

  2. A base case is a situation where a recursive function does not need to make a recursive call to solve the problem. A recursive case is a situation where the recursive function does call itself. Recursive functions need both cases because the recursive case is called repeatedly until the base case is reached, stopping the chain of recursive calls.

  3. Output produced by the mystery1 function by each call:

    1. 1
    2. 1, 2
    3. 1, 3
    4. 1, 2, 4
    5. 1, 2, 4, 8, 16
    6. 1, 3, 7, 15, 30
    7. 1, 3, 6, 12, 25, 50, 100
  4. Output produced by the mystery2 function by each call:

    1. 113
    2. 140, 70
    3. 168, 84, 42
    4. 120, 60, 30
    5. 160, 80, 40, 20, 10
  5. Output produced by the mystery_x_y function by each call:

    1. 4
    2. 8, 4, 8
    3. 16, 8, 16
    4. 12, 8, 4, 8, 12
    5. 12, 9, 6, 3, 6, 9, 12
  6. Recursive version of double_reverse function:

    def double_reverse(s):
        if len(s) > 0:
            last = s[-1]
            print(last, end="")
            print(last, end="")
            double_reverse(s[0:-1])
    
  7. The version of the pow function shown does not have any base case, so the recursive calls will never stop. It can be fixed by adding a check for y == 0 that does not make a recursive call.

  8. The second version of the pow function is more efficient than the first because it requires fewer recursive calls. Both versions are recursive.

  9. Value returned by the mystery4 function for each call:

    1. 6
    2. 4
    3. 7
    4. 0
    5. 1
  10. Value returned by the mystery5 method for each call:

    1. 57
    2. 1029
    3. -74
    4. 2438
    5. 132483
  11. Recursive version of factorial function:

    def factorial(n):
        if n == 0:
            return 1
        else:
            return n * factorial(n - 1)
    
  12. The base case if statement has a bug: It should test for numbers less than 10, not greater. The following is the correct line:

    if n < 10:
    
  13. When the parameters needed for recursion don't match those that make sense for the client to pass, use a function with the signature desired by the client and a helper function with the signature needed for the recursion.

  14. The following version of the fibonacci code has improved efficiency:

    def fibonacci(n):
        if n <= 2:
            return 1
        else:
            return fibonacci2(n, 3, 1, 1)
    
    def fibonacci2(n, i, prev, curr):
        if n == i:
            return prev + curr
        else:
            return fibonacci2(n, i + 1, curr, prev + curr)
    
  15. A fractal is an image that is recursively constructed to contain smaller versions of itself. Recursive functions are useful when drawing fractal images because they can elegantly express the recursive nature of the images.

  16. Code to create and draw a regular hexagon:

    def draw_hexagon(panel, x, y, size):
        p1 = (x, y + size // 2)
        p2 = (x + size // 3, y)
        p3 = (x + 2 * size // 3, y)
        p4 = (x + size, y + size // 2)
        p5 = (x + 2 * size // 3, y + size)
        p5 = (x + size // 3, y + size)
        panel.fill_polygon(p1, p2, p3, p4, p5)
    
  17. Recursion is an effective way to implement a backtracking algorithm because the memory of decisions and points to go back to are represented by the recursive call stack. The pattern of "choose, explore, un-choose is elegantly represented by recursive calls for each individual choice.

  18. A decision tree is a description of the set of choices that can be made by a recursive backtracking function at any point in the algorithm.

  19. Decision tree that would have resulted for Figure for paths to (1, 2) if the backtracking solution had explored NE first instead of last in the recursive explore function:

    start (0, 0)
    |
    +--- NE (1, 1)
    |    |
    |    +--- NE NE (2, 2)
    |    |
    |    +--- NE N (1, 2) - output
    |    |
    |    +--- NE E (2, 1)
    |
    +--- N (0, 1)
    |    |
    |    +--- N NE (1, 2) - output
    |    |
    |    +--- N N (0, 2)
    |    |    |
    |    |    +--- N N NE (1, 3)
    |    |    |
    |    |    +--- N N N (0, 3)
    |    |    |
    |    |    +--- N N E (1, 2) - output
    |    |
    |    +--- N E (1, 1)
    |         |
    |         +--- N E NE (2, 2)
    |         |
    |         +--- N E N (1, 2) - output
    |         |
    |         +--- N E E (2, 1)
    |
    +--- E (1, 0)
         |
         +--- E NE (2, 1)
         |
         +--- E N (1, 1)
         |    |
         |    +--- E N NE (2, 2)
         |    |
         |    +--- E N N (1, 2) - output
         |    |
         |    +--- E N E (2, 1)
         |
         +--- E E (2, 0)
    
  20. If the solution had explored NE first instead of last, the solutions would have been printed in this order:

    moves: NE N
    moves: N NE
    moves: N N E
    moves: N E N
    moves: E N N
    
  21. There are 64 entries at the second level of the full tree. There are 512 entries at the third level of the full tree.

  22. If our 8 Queens algorithm tried every possible square on the board for placing each queen, there would be (64*63*62*61*60*59*58*57) = 178,462,987,637,760 entries are there at the 8th and final level of the full tree. Our algorithm avoids such a huge number of choices by only placing one queen in each column of the board.

  23. The 8 Queens explore function stops once it finds one solution to the problem. This is because the code has the following lines around its recursive call:

    if explore(b, col + 1):
        return True
    

    The code could be modified so that it would find and output every solution to the problem by changing that code to the following:

    explore(b, col + 1)
    

    And changing the base case to the following:

    if col > len(b):
        print("One solution is as follows:")
        b.print() 
        return True
    

Chapter 10

  1. You can perform a sequential search over the list using a loop, or you can sort the list using sort and then perform a binary search over it using bisect.bisect_left.

  2. Closest value to the number of elements that the binary search algorithm will need to examine on a list of one million integers:

    e. less than 1% (10,000 or fewer)
  3. You should use binary search if the list is sorted. Otherwise you will have to use sequential search.

  4. O(log N)

  5. O(N)

  6. O(N2)

  7. O(N2)

  8. Complexity classes of the given algorithms in terms of N:

    1. O(N)
    2. O(N2)
    3. O(N)
    4. O(N)
    5. O(N)
    6. O(1)
  9. Complexity classes of the given statements:

    1. O(N log N)
    2. O(N2)
    3. O(N2 log N)
    4. O(N)
    5. O(N^4)
    6. O(N)
    7. O(N!)
  10. The runtime complexity of both sequential searches is O(N).

  11. Binary search requires a sorted dataset because it uses the ordering to jump to the next index. If the elements are out of order, the search isn't guaranteed to find the target element.

  12. A binary search of 60 elements examines at most 6 elements, because log2 60 (when rounded up) equals 6.

    1. The algorithm will examine index 4 and will return 4.
    2. The algorithm will examine indexes 4 and 6 and will return 6.
    3. The algorithm will examine indexes 4, 6, and 7 and will return 7.
    4. The algorithm will examine indexes 4, 2, 1, and 0 and will return 0.
  13. The algorithm will examine indexes 4, 6, and 5 and will return -1. The algorithm doesn't work properly because the input array isn't sorted.

  14. The binary search algorithm will examine the following indexes and return the following values for each search:

    1. -5: examines 6, 2, 4, 3; returns -4
    2. 0: examines 6; returns 6
    3. 11: examines 6, 10, 8, 9; returns -10
    4. -100: examines 6, 2, 0; returns -1
  15. After a single pass of the selection sort algorithm (a single swap), the state of the list is:

    d. [-4, 17, 3, 94, 46, 8, 29, 12]
  16. All steps of selection sort algorithm:

    1. [29, 17, 3, 94, 46, 8, -4, 12]
      [-4, 17, 3, 94, 46, 8, 29, 12]
      [-4, 3, 17, 94, 46, 8, 29, 12]
      [-4, 3, 8, 94, 46, 17, 29, 12]
      [-4, 3, 8, 12, 46, 17, 29, 94]
      [-4, 3, 8, 12, 17, 46, 29, 94]
      [-4, 3, 8, 12, 17, 29, 46, 94]
      
    2. [33, 14, 3, 95, 47, 9, -42, 13]
      [-42, 14, 3, 95, 47, 9, 33, 13]
      [-42, 3, 14, 95, 47, 9, 33, 13]
      [-42, 3, 9, 95, 47, 14, 33, 13]
      [-42, 3, 9, 13, 47, 14, 33, 95]
      [-42, 3, 9, 13, 14, 47, 33, 95]
      [-42, 3, 9, 13, 14, 33, 47, 95]
      
    3. [7, 1, 6, 12, -3, 8, 4, 21, 2, 30, -1, 9]
      [-3, 1, 6, 12, 7, 8, 4, 21, 2, 30, -1, 9]
      [-3, -1, 6, 12, 7, 8, 4, 21, 2, 30, 1, 9]
      [-3, -1, 1, 12, 7, 8, 4, 21, 2, 30, 6, 9]
      [-3, -1, 1, 2, 7, 8, 4, 21, 12, 30, 6, 9]
      [-3, -1, 1, 2, 4, 8, 7, 21, 12, 30, 6, 9]
      [-3, -1, 1, 2, 4, 6, 7, 21, 12, 30, 8, 9]
      [-3, -1, 1, 2, 4, 6, 7, 21, 12, 30, 8, 9]
      [-3, -1, 1, 2, 4, 6, 7, 8, 12, 30, 21, 9]
      [-3, -1, 1, 2, 4, 6, 7, 8, 9, 30, 21, 12]
      [-3, -1, 1, 2, 4, 6, 7, 8, 9, 12, 21, 30]
      
    4. [6, 7, 4, 8, 11, 1, 10, 3, 5, 9]
      [1, 7, 4, 8, 11, 6, 10, 3, 5, 9]
      [1, 3, 4, 8, 11, 6, 10, 7, 5, 9]
      [1, 3, 4, 8, 11, 6, 10, 7, 5, 9]
      [1, 3, 4, 5, 11, 6, 10, 7, 8, 9]
      [1, 3, 4, 5, 6, 11, 10, 7, 8, 9]
      [1, 3, 4, 5, 6, 7, 10, 11, 8, 9]
      [1, 3, 4, 5, 6, 7, 8, 11, 10, 9]
      [1, 3, 4, 5, 6, 7, 8, 9, 10, 11]
      
  17. A merge sort of 32 elements will generate 63 total calls to merge_sort and will perform the merge operation 31 times.

    1. State of the elements after five passes of the outermost loop of selection sort have occurred:

      [1, 2, 3, 4, 5, 11, 9, 7, 8, 10]
      
    2. Trace of merge sort algorithm:

      [7,   2,   8,   4,   1,   11,   9,   5,   3,   10]
      [7,   2,   8,   4,   1], [11,   9,   5,   3,   10]
      [7,   2], [8,   4,   1], [11,   9], [5,   3,   10] 
      [7], [2], [8], [4,   1], [11], [9], [5], [3,   10]
                     [4], [1],                 [3], [10]
                [8], [1,   4],            [5], [3,   10]
      [2,   7], [1,   4,   8], [9,   11], [3,   5,   10]
      [1,   2,   4,   7,   8], [3,    5,   9,  10,   11]
      [1,   2,   3,   4,   5,   7,    8,   9,  10,   11]
      
    1. State of the elements after five passes of the outermost loop of selection sort have occurred:

      [-3, -1, 1, 2, 4, 8, 7, 21, 12, 30, 6, 9]
      
    2. Trace of merge sort algorithm:

      [7,   1,   6,   12,   -3,   8,    4,   21,   2,   30,   -1,   9]
      [7,   1,   6,   12,   -3,   8],  [4,   21,   2,   30,   -1,   9]
      [7,   1,   6], [12,   -3,   8],  [4,   21,   2], [30,   -1,   9]
      [7], [1,   6], [12], [-3,   8],  [4], [21,   2], [30], [-1,   9]
           [1], [6],       [-3], [8],       [21], [2],       [-1], [9]
      [7], [1,   6], [12], [-3,   8],  [4], [2,   21], [30], [-1,   9]
      [1,   6,   7], [-3,    8,  12],  [2,   4,   21], [-1,    9,  30]
      [-3,  1,   6,    7,    8,  12], [-1,   2,    4,    9,   21,  30]
      [-3, -1,   1,    2,    4,   6,    7,   8,    9,   12,   21,  30]
      
  18. The following statement about sorting and big-Oh is true:

    b. Merge sort achieves an O(N log N) runtime by dividing the list in half at each step and then recursively sorting and merging the halves back together.
  19. Traces of merge sort algorithm:

    1. [29, 17, 3, 94, 46, 8, -4, 12]
      [29, 17, 3, 94], [46, 8, -4, 12]
      [29, 17], [3, 94], [46, 8], [-4, 12]
      [29], [17], [3], [94], [46], [8], [-4], [12]
      [17, 29], [3, 94], [8, 46], [-4, 12]
      [3, 17, 29, 94], [-4, 8, 12, 46]
      [-4, 3, 8, 12, 17, 29, 46, 94]
      
    2. [6, 5, 3, 7, 1, 8, 4, 2]
      [6, 5, 3, 7], [1, 8, 4, 2]
      [6, 5], [3, 7], [1, 8], [4, 2]
      [6], [5], [3], [7], [1], [8], [4], [2]
      [5, 6], [3, 7], [1, 8], [2, 4]
      [3, 5, 6, 7], [1, 2, 4, 8]
      [1, 2, 3, 4, 5, 6, 7, 8]
      
    3. [33, 14, 3, 95, 47, 9, -42, 13]
      [33, 14, 3, 95], [47, 9, -42, 13]
      [33, 14], [3, 95], [47, 9], [-42, 13]
      [33], [14], [3], [95], [47], [9], [-42], [13]
      [14, 33], [3, 95], [9, 47], [-42, 13]
      [14, 33], [3, 95], [9, 47], [-42, 13]
      [3, 14, 33, 95], [-42, 9, 13, 47]
      [-42, 3, 9, 13, 14, 33, 47, 95]
      

Chapter 11

  1. Procedural programming treats a program as a sequence of actions or commands to perform. Object-oriented programming looks at a program as a group of interacting entities named objects that each keep track of related data and behavior.

  2. An object is an entity that encapsulates data and behavior that operates on the data. A class is the blueprint for a type of object, specifying what data and behavior the object will have and how to construct it.

  3. Output of the program:

    14 14
    7 9 14 2
    18 18
    7 9 14 18
    
  4. The state of a Calculator object might include the number that has just been computed, as well as another number that is currently being input. A more complex Calculator object might also include a memory feature that stores an additional value. The behavior of a Calculator object might include methods to add, subtract, multiply, divide, and perhaps carryout other math operations (such as exponentiation, logarithms, and trigonometric functions like sine and cosine).

  5. An attribute is a variable that exists inside of an object. A parameter is a variable inside a method whose value is passed in from outside. Attributes have different syntax because they are declared at the top of a class and not in a method's header. An attribute's scope is throughout the class, while a parameter's scope is limited to the method.

  6. Name class that represents a person's name:

    # A Name object represents a name such as "John Q. Public".
    # This version contains only data attributes and a constructor.
    class Name:
        def __init__(self, first, middle, last):
            self.first_name = first
            self.middle_initial = middle
            self.last_name = last
    
  7. An accessor provides the client access to some data in the object, while a mutator lets the client change the object's state in some way. Accessors' names often begin with "get" or "is", while mutators' names often begin with "set".

  8. Correct syntax for calling compute_interest method on a BankAccount object:

    d. result = acct.compute_interest(42)
  9. Name class with two methods:

    # A Name object represents a name such as "John Q. Public".
    # This version adds the methods get_normal_order and get_reverse_order.
    class Name:
        def __init__(self, first, middle, last):
            self.first_name = first
            self.middle_initial = middle
            self.last_name = last
    
        # The name in normal order such as "John Q. Public".
        def normal_order(self):
            return self.first_name + " " + self.middle_initial + ". " + self.last_name
    
        # The name in reverse order such as "Public, John Q.".
        def reverse_order(self):
            return self.last_name + ", " + self.first_name + " " + self.middle_initial + "."
    
  10. To make the objects of your class printable, define a __str__ method in it.

  11. The print statement is equivalent to the following:

    c. print(d1.__str__())
  12. __str__ method for Name class:

    # Returns a string representation of this Name, such as "John Q. Public".
    def __str__(self):
        return self.first_name + " " + self.middle_initial + ". " + self.last_name
    
    or:
    # Returns a string representation of this Name, such as "John Q. Public".
    def __str__(self):
        return self.normal_order()
    
  13. Abstraction is the ability to focus on a problem at a high level without worrying about the minor details. Objects provide abstraction by giving us more powerful pieces of data that have sophisticated behavior without having to manage and manipulate the data directly.

  14. To access private attributes, create properties that return their values. For example, add a name property to access the _name data attribute of an object. You can also write accessor methods that return data from the object.

  15. Encapsulated version of Name class:

    # A Name object represents a name such as "John Q. Public".
    # This version is encapsulated with underscored attributes and properties.
    class Name:
        # Initializes a new Name with the given values.
        def __init__(self, first, middle, last):
            self._first = first
            self._middle = middle
            self._last = last
    
        # Returns the person's first name.
        @property
        def first_name(self):
            return self._first
    
        # Returns the person's middle initial.
        @property
        def middle_initial(self):
            return self._middle
    
        # Returns the person's last name.
        @property
        def last_name(self):
            return self._last
    
        # The name in normal order such as "John Q. Public"
        def normal_order(self):
            return self._first + " " + self._middle + ". " + self._last
    
        # The name in reverse order such as "Public, John Q.".
        def reverse_order(self):
            return self._last + ", " + self._first + " " + self._middle + "."
    
        # The name in normal order such as "John Q. Public"
        def __str__(self):
            return self.normal_order()
    
  16. Property setters for Name class:

    class Name:
        ...
        
        # Sets the first name to the given value.
        @first_name.setter
        def first_name(self, value):
            self._first = value
    
        # Sets the middle initial to the given value.
        @middle_initial.setter
        def middle_initial(self, value):
            self._middle = value
    
        # Sets the last name to the given value.
        @last_name.setter
        def last_name(self, value):
            self._last = value
    
  17. Encapsulation allows you to change a class's internal implementation without changing its external view to clients. When a class is encapsulated clients should not directly access its attributes, so changing those fields will not disturb client behavior as long as the external view (method behavior) is consistent.

  18. Cohesion is the concept of how well a class's contents go together. You can tell that a class is cohesive when each of its attributes stores important state related to the object and each method interacts with that state in some way to produce useful behavior.

  19. We did not place console I/O code into our Stock class because doing so would force clients to use those exact I/O messages. By keeping I/O code out of Stock, we kept it independent from its clients.

  20. Property setters for Stock class:

    @symbol.setter
    def symbol(self, value):
        self._symbol = value
    
    @total_cost.setter
    def total_cost(self, value):
        if value >= 0.0:
            self._total_cost = value
    
    @total_shares.setter
    def total_shares(self, value):
        if value >= 0:
            self._total_shares = value
    

Chapter 12

  1. Because functional programming focuses so much on individual functions, the community of programmers who use functional programming regularly have concluded that side effects should be avoided when possible. Functions without side effects are easy to reason about, reuse, combine, and parallelize.

  2. Calling print is considered a side effect because it produces a noticeable outcome, namely printing output to the console. So you can detect whether a given function was called or not by looking for output. This doesn't mean that printing output is a bad thing, merely that it is a side effect.

  3. The function's side effect is that it modifies the list that was passed in. It could be changed to remove side effects by having it return the new list state rather than changing the existing list.

    # Returns a new list whose values are twice as large as
    # the values of all elements in the given list.
    def double_all(lis):
        result = [0] * len(lis)
        for i in range(len(lis)):
            result[i] = 2 * lis[i]
        return result
    
  4. lambda num: num * num
    
  5. lambda a, b: max(a, b)
    
  6. lambda first, last: last + ", " + first
    
  7. [10, 28, 33, 28, 49, 56, 49]
    

  8. [num for num in numbers if num >= 0]
    
  9. [w for w in words if 3 <= len(w) <= 4]
    
  10. [line for line in open("notes.txt").readlines() if len(line) >= 40]
    
  11. A bound variable is inside the lambda, typically one of its parameters. A free variable is a variable referred to in the lambda's code that is declared outside the lambda and enclosed into its closure.

  12. The free variable is a, and the bound variables are b (the second one) and c.